aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2019-07-08 11:11:01 -0400
committerSam Wilkins <samwilkins333@gmail.com>2019-07-08 11:11:01 -0400
commit6145a7ef060aeb1b815b2da7acba4d8f9d2e2bdc (patch)
treeb63ceff150fe9e5f7383402a99da8d8f8f31cb05
parent5ce2d59b4544a7473a910c121daf0c34d1546214 (diff)
parent400c525875af607dd76d7ec46949fedc418caabf (diff)
merged with master
-rw-r--r--deploy/assets/downarrow.pngbin0 -> 3363 bytes
-rw-r--r--src/client/documents/Documents.ts29
-rw-r--r--src/client/northstar/dash-nodes/HistogramBox.tsx2
-rw-r--r--src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx2
-rw-r--r--src/client/util/DragManager.ts4
-rw-r--r--src/client/util/RichTextSchema.tsx11
-rw-r--r--src/client/util/Scripting.ts45
-rw-r--r--src/client/util/SearchUtil.ts33
-rw-r--r--src/client/util/TooltipTextMenu.scss2
-rw-r--r--src/client/util/TooltipTextMenu.tsx35
-rw-r--r--src/client/views/DocumentDecorations.scss22
-rw-r--r--src/client/views/DocumentDecorations.tsx32
-rw-r--r--src/client/views/EditableView.tsx3
-rw-r--r--src/client/views/InkingControl.tsx3
-rw-r--r--src/client/views/Main.scss24
-rw-r--r--src/client/views/MainOverlayTextBox.tsx12
-rw-r--r--src/client/views/MainView.tsx121
-rw-r--r--src/client/views/OverlayView.tsx47
-rw-r--r--src/client/views/ScriptBox.scss17
-rw-r--r--src/client/views/ScriptBox.tsx44
-rw-r--r--src/client/views/Templates.tsx3
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx2
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx9
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx21
-rw-r--r--src/client/views/collections/CollectionStackingView.scss14
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx106
-rw-r--r--src/client/views/collections/CollectionSubView.tsx10
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx103
-rw-r--r--src/client/views/collections/CollectionVideoView.tsx1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx82
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.scss2
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx20
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx33
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx4
-rw-r--r--src/client/views/nodes/DocumentView.tsx11
-rw-r--r--src/client/views/nodes/FieldView.tsx2
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx13
-rw-r--r--src/client/views/nodes/ImageBox.tsx7
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx12
-rw-r--r--src/client/views/nodes/LinkEditor.tsx1
-rw-r--r--src/client/views/nodes/PDFBox.tsx2
-rw-r--r--src/client/views/pdf/Annotation.tsx2
-rw-r--r--src/client/views/pdf/PDFMenu.tsx19
-rw-r--r--src/client/views/pdf/PDFViewer.tsx29
-rw-r--r--src/client/views/pdf/Page.tsx4
-rw-r--r--src/client/views/presentationview/PresentationElement.tsx2
-rw-r--r--src/client/views/search/SearchItem.tsx62
-rw-r--r--src/debug/Viewer.tsx9
-rw-r--r--src/new_fields/Doc.ts25
-rw-r--r--src/new_fields/List.ts5
-rw-r--r--src/new_fields/RichTextField.ts2
-rw-r--r--src/new_fields/ScriptField.ts4
-rw-r--r--src/new_fields/URLField.ts11
-rw-r--r--src/new_fields/util.ts2
-rw-r--r--src/server/GarbageColletor.ts51
-rw-r--r--src/server/authentication/models/current_user_utils.ts39
-rw-r--r--src/server/database.ts14
-rw-r--r--src/server/index.ts7
-rw-r--r--tance.jumpToDocument(linkedFwdDocs[altKey  1  0], ctrlKey, false, document = this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey  1  0], targetContext);792
59 files changed, 920 insertions, 1105 deletions
diff --git a/deploy/assets/downarrow.png b/deploy/assets/downarrow.png
new file mode 100644
index 000000000..3c59ff5b1
--- /dev/null
+++ b/deploy/assets/downarrow.png
Binary files differ
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 26b5498a2..26a6f202c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -37,6 +37,7 @@ import { RouteStore } from "../../server/RouteStore";
import { LinkManager } from "../util/LinkManager";
import { DocumentManager } from "../util/DocumentManager";
import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox";
+import { Scripting } from "../util/Scripting";
var requestImageSize = require('../util/request-image-size');
var path = require('path');
@@ -78,7 +79,7 @@ export interface DocumentOptions {
backgroundLayout?: string;
curPage?: number;
documentText?: string;
- borderRounding?: number;
+ borderRounding?: string;
schemaColumns?: List<string>;
dockingConfig?: string;
dbDoc?: Doc;
@@ -94,7 +95,7 @@ export namespace DocUtils {
if (target === CurrentUserUtils.UserDocument) return;
UndoManager.RunInBatch(() => {
- let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 });
+ let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: "100%" });
linkDoc.type = DocTypes.LINK;
let linkDocProto = Doc.GetProto(linkDoc);
@@ -440,26 +441,6 @@ export namespace Docs {
`);
}
- /*
-
- this template requires an additional style setting on the collectionView-cont to make the layout relative
-
-.collectionView-cont {
- position: relative;
- width: 100%;
- height: 100%;
}
- */
- function Percentaption() {
- return (`
- <div>
- <div style="margin:auto; height:85%; width:85%;">
- {layout}
- </div>
- <div style="height:15%; width:100%; position:absolute">
- <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} renderDepth={renderDepth}/>
- </div>
- </div>
- `);
- }
-} \ No newline at end of file
+
+Scripting.addGlobal("Docs", Docs); \ No newline at end of file
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index a60eaea85..b81eafbee 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -36,7 +36,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
@computed public get HistogramResult(): HistogramResult { return this.HistoOp.Result as HistogramResult; }
@observable public SizeConverter: SizeConverter = new SizeConverter();
- @computed get createOperationParamsCache() { trace(); return this.HistoOp.CreateOperationParameters(); }
+ @computed get createOperationParamsCache() { return this.HistoOp.CreateOperationParameters(); }
@computed get BinRanges() { return this.HistogramResult ? this.HistogramResult.binRanges : undefined; }
@computed get ChartType() {
return !this.BinRanges ? ChartType.SinglePoint : this.BinRanges[0] instanceof AggregateBinRange ?
diff --git a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
index 350987695..5a16b3782 100644
--- a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
@@ -62,7 +62,6 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
}
private renderGridLinesAndLabels(axis: number) {
- trace();
if (!this.props.HistoBox.SizeConverter.Initialized) {
return (null);
}
@@ -111,7 +110,6 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
x={transXpercent} width={`${widthXpercent}`} y={transYpercent} height={`${heightYpercent}`} fill={color ? `${LABColor.RGBtoHexString(color)}` : "transparent"} />);
}
render() {
- trace();
return <div className="histogramboxprimitives-container">
{this.xaxislines}
{this.yaxislines}
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 7dc48fb78..a269d7eba 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -315,11 +315,13 @@ export namespace DragManager {
scaleYs.push(scaleY);
let dragElement = ele.cloneNode(true) as HTMLElement;
dragElement.style.opacity = "0.7";
+ dragElement.style.borderRadius = getComputedStyle(ele).borderRadius;
dragElement.style.position = "absolute";
dragElement.style.margin = "0";
dragElement.style.top = "0";
dragElement.style.bottom = "";
dragElement.style.left = "0";
+ dragElement.style.transition = "none";
dragElement.style.color = "black";
dragElement.style.transformOrigin = "0 0";
dragElement.style.zIndex = globalCssVariables.contextMenuZindex;// "1000";
@@ -447,7 +449,7 @@ export namespace DragManager {
x: e.x,
y: e.y,
data: dragData,
- mods: e.altKey ? "AltKey" : ""
+ mods: e.altKey ? "AltKey" : e.ctrlKey ? "CtrlKey" : ""
}
})
);
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 63c879d67..2a57180d3 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -348,6 +348,17 @@ export const marks: { [index: string]: MarkSpec } = {
}]
},
+ pFontColor: {
+ attrs: {
+ color: { default: "yellow" }
+ },
+ parseDOM: [{ style: 'background: #d9dbdd' }],
+ toDOM: (node) => {
+ return ['span', {
+ style: `color: ${node.attrs.color}`
+ }];
+ }
+ },
/** FONT SIZES */
pFontSize: {
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 30a05154a..3156c4f43 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -7,12 +7,7 @@ let ts = (window as any).ts;
// @ts-ignore
import * as typescriptlib from '!!raw-loader!./type_decls.d';
-import { Docs } from "../documents/Documents";
import { Doc, Field } from '../../new_fields/Doc';
-import { ImageField, PdfField, VideoField, AudioField } from '../../new_fields/URLField';
-import { List } from '../../new_fields/List';
-import { RichTextField } from '../../new_fields/RichTextField';
-import { ScriptField, ComputedField } from '../../new_fields/ScriptField';
export interface ScriptSucccess {
success: true;
@@ -38,6 +33,34 @@ export interface CompileError {
errors: any[];
}
+export namespace Scripting {
+ export function addGlobal(global: { name: string }): void;
+ export function addGlobal(name: string, global: any): void;
+ export function addGlobal(nameOrGlobal: any, global?: any) {
+ let n: string;
+ let obj: any;
+ if (global !== undefined && typeof nameOrGlobal === "string") {
+ n = nameOrGlobal;
+ obj = global;
+ } else if (nameOrGlobal && typeof nameOrGlobal.name === "string") {
+ n = nameOrGlobal.name;
+ obj = nameOrGlobal;
+ } else {
+ throw new Error("Must either register an object with a name, or give a name and an object");
+ }
+ if (scriptingGlobals.hasOwnProperty(n)) {
+ throw new Error(`Global with name ${n} is already registered, choose another name`);
+ }
+ scriptingGlobals[n] = obj;
+ }
+}
+
+export function scriptingGlobal(constructor: { new(...args: any[]): any }) {
+ Scripting.addGlobal(constructor);
+}
+
+const scriptingGlobals: { [name: string]: any } = {};
+
export type CompileResult = CompiledScript | CompileError;
function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult {
const errors = diagnostics.some(diag => diag.category === ts.DiagnosticCategory.Error);
@@ -45,9 +68,11 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
return { compiled: false, errors: diagnostics };
}
- let fieldTypes = [Doc, ImageField, PdfField, VideoField, AudioField, List, RichTextField, ScriptField, ComputedField, CompileScript];
- let paramNames = ["Docs", ...fieldTypes.map(fn => fn.name)];
- let params: any[] = [Docs, ...fieldTypes];
+ let paramNames = Object.keys(scriptingGlobals);
+ let params = paramNames.map(key => scriptingGlobals[key]);
+ // let fieldTypes = [Doc, ImageField, PdfField, VideoField, AudioField, List, RichTextField, ScriptField, ComputedField, CompileScript];
+ // let paramNames = ["Docs", ...fieldTypes.map(fn => fn.name)];
+ // let params: any[] = [Docs, ...fieldTypes];
let compiledFunction = new Function(...paramNames, `return ${script}`);
let { capturedVariables = {} } = options;
let run = (args: { [name: string]: any } = {}): ScriptResult => {
@@ -178,4 +203,6 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
let diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics);
return Run(outputText, paramNames, diagnostics, script, options);
-} \ No newline at end of file
+}
+
+Scripting.addGlobal(CompileScript); \ No newline at end of file
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index ac2eb72e7..338628960 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -29,11 +29,16 @@ export namespace SearchUtil {
return { docs, numFound };
}
- export async function GetAliasesOfDocument(doc: Doc): Promise<Doc[]> {
- const proto = await Doc.GetT(doc, "proto", Doc, true);
- const protoId = (proto || doc)[Id];
- const result = await Search(`proto_i:"${protoId}"`, true);
- return result.docs;
+ export async function GetAliasesOfDocument(doc: Doc): Promise<Doc[]>;
+ export async function GetAliasesOfDocument(doc: Doc, returnDocs: false): Promise<string[]>;
+ export async function GetAliasesOfDocument(doc: Doc, returnDocs = true): Promise<Doc[] | string[]> {
+ const proto = Doc.GetProto(doc);
+ const protoId = proto[Id];
+ if (returnDocs) {
+ return (await Search(`proto_i:"${protoId}"`, returnDocs)).docs;
+ } else {
+ return (await Search(`proto_i:"${protoId}"`, returnDocs)).ids;
+ }
// return Search(`{!join from=id to=proto_i}id:${protoId}`, true);
}
@@ -41,4 +46,22 @@ export namespace SearchUtil {
const results = await Search(`proto_i:"${doc[Id]}"`, true);
return results.docs;
}
+
+ export async function GetContextsOfDocument(doc: Doc): Promise<{ contexts: Doc[], aliasContexts: Doc[] }> {
+ const docContexts = (await Search(`data_l:"${doc[Id]}"`, true)).docs;
+ const aliases = await GetAliasesOfDocument(doc, false);
+ const aliasContexts = (await Promise.all(aliases.map(doc => Search(`data_l:"${doc}"`, true))));
+ const contexts = { contexts: docContexts, aliasContexts: [] as Doc[] };
+ aliasContexts.forEach(result => contexts.aliasContexts.push(...result.docs));
+ return contexts;
+ }
+
+ export async function GetContextIdsOfDocument(doc: Doc): Promise<{ contexts: string[], aliasContexts: string[] }> {
+ const docContexts = (await Search(`data_l:"${doc[Id]}"`, false)).ids;
+ const aliases = await GetAliasesOfDocument(doc, false);
+ const aliasContexts = (await Promise.all(aliases.map(doc => Search(`data_l:"${doc}"`, false))));
+ const contexts = { contexts: docContexts, aliasContexts: [] as string[] };
+ aliasContexts.forEach(result => contexts.aliasContexts.push(...result.ids));
+ return contexts;
+ }
} \ No newline at end of file
diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss
index b10573b3e..40ac3abb9 100644
--- a/src/client/util/TooltipTextMenu.scss
+++ b/src/client/util/TooltipTextMenu.scss
@@ -248,7 +248,7 @@
transform: translateY(-85px);
pointer-events: all;
height: 30px;
- width:500px;
+ width:550px;
.ProseMirror-example-setup-style hr {
padding: 2px 10px;
border: none;
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 9f8d0b2f6..cb7ed976a 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -32,7 +32,6 @@ export class TooltipTextMenu {
private fontSizeToNum: Map<MarkType, number>;
private fontStylesToName: Map<MarkType, string>;
private listTypeToIcon: Map<NodeType, string>;
- private link: HTMLAnchorElement;
private linkEditor?: HTMLDivElement;
private linkText?: HTMLDivElement;
@@ -89,6 +88,7 @@ export class TooltipTextMenu {
});
});
+ this.updateLinkMenu();
//list of font styles
this.fontStylesToName = new Map();
@@ -111,7 +111,8 @@ export class TooltipTextMenu {
this.fontSizeToNum.set(schema.marks.p32, 32);
this.fontSizeToNum.set(schema.marks.p48, 48);
this.fontSizeToNum.set(schema.marks.p72, 72);
- //this.fontSizeToNum.set(schema.marks.pFontSize,schema.marks.pFontSize.)
+ this.fontSizeToNum.set(schema.marks.pFontSize, 10);
+ this.fontSizeToNum.set(schema.marks.pFontSize, 10);
this.fontSizes = Array.from(this.fontSizeToNum.keys());
//list types
@@ -120,11 +121,6 @@ export class TooltipTextMenu {
this.listTypeToIcon.set(schema.nodes.ordered_list, "1)");
this.listTypes = Array.from(this.listTypeToIcon.keys());
- this.link = document.createElement("a");
- this.link.target = "_blank";
- this.link.style.color = "white";
- //this.tooltip.appendChild(this.link);
-
this.tooltip.appendChild(this.createLink().render(this.view).dom);
this.tooltip.appendChild(this.createStar().render(this.view).dom);
@@ -190,6 +186,7 @@ export class TooltipTextMenu {
updateLinkMenu() {
if (!this.linkEditor || !this.linkText) {
this.linkEditor = document.createElement("div");
+ this.linkEditor.className = "ProseMirror-icon menuicon";
this.linkEditor.style.color = "black";
this.linkText = document.createElement("div");
this.linkText.style.cssFloat = "left";
@@ -230,8 +227,9 @@ export class TooltipTextMenu {
};
this.linkDrag = document.createElement("img");
this.linkDrag.src = "https://seogurusnyc.com/wp-content/uploads/2016/12/link-1.png";
- this.linkDrag.style.width = "20px";
- this.linkDrag.style.height = "20px";
+ this.linkDrag.style.width = "15px";
+ this.linkDrag.style.height = "15px";
+ this.linkDrag.title = "Drag to create link";
this.linkDrag.style.color = "black";
this.linkDrag.style.background = "black";
this.linkDrag.style.cssFloat = "left";
@@ -249,10 +247,10 @@ export class TooltipTextMenu {
hideSource: false
});
};
- // this.linkEditor.appendChild(this.linkDrag);
+ this.linkEditor.appendChild(this.linkDrag);
// this.linkEditor.appendChild(this.linkText);
// this.linkEditor.appendChild(linkBtn);
- //this.tooltip.appendChild(this.linkEditor);
+ this.tooltip.appendChild(this.linkEditor);
}
let node = this.view.state.selection.$from.nodeAfter;
@@ -443,16 +441,24 @@ export class TooltipTextMenu {
enable(state) { return !state.selection.empty; },
run: (state, dispatch, view) => {
// to remove link
+ let curLink = "";
if (this.markActive(state, markType)) {
- toggleMark(markType)(state, dispatch);
- return true;
+
+ let { from, $from, to, empty } = state.selection;
+ let node = state.doc.nodeAt(from);
+ node && node.marks.map(m => {
+ m.type === markType && (curLink = m.attrs.href);
+ })
+ //toggleMark(markType)(state, dispatch);
+ //return true;
}
// to create link
openPrompt({
title: "Create a link",
fields: {
href: new TextField({
- label: "Link target",
+ value: curLink,
+ label: "Link Target",
required: true
}),
title: new TextField({ label: "Title" })
@@ -602,7 +608,6 @@ export class TooltipTextMenu {
}
}
this.view.dispatch(this.view.state.tr.setStoredMarks(this._activeMarks));
- this.updateLinkMenu();
}
//finds all active marks on selection in given group
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index ba9f32d7d..2d430512b 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -26,6 +26,14 @@ $linkGap : 3px;
opacity: 0.8;
}
+ .documentDecorations-radius {
+ pointer-events: auto;
+ background: black;
+ opacity: 0.8;
+ transform: translate(10px, 10px);
+ grid-row: 4;
+ }
+
#documentDecorations-topLeftResizer,
#documentDecorations-leftResizer,
#documentDecorations-bottomLeftResizer {
@@ -44,11 +52,25 @@ $linkGap : 3px;
grid-column-start: 5;
grid-column-end: 7;
}
+
+ #documentDecorations-borderRadius{
+ grid-column-start: 5;
+ grid-column-end: 7;
+ border-radius: 100%;
+ .borderRadiusTooltip{
+ width:10px;
+ height:10px;
+ position:absolute;
+ }
+ }
#documentDecorations-topLeftResizer,
#documentDecorations-bottomRightResizer {
cursor: nwse-resize;
}
+ #documentDecorations-bottomRightResizer {
+ grid-row:4;
+ }
#documentDecorations-topRightResizer,
#documentDecorations-bottomLeftResizer {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3df520428..c7990647a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -341,6 +341,37 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
iconDoc.y = where[1] + NumCast(selView.props.Document.y);
}
+ _radiusDown = [0, 0];
+ @action
+ onRadiusDown = (e: React.PointerEvent): void => {
+ e.stopPropagation();
+ if (e.button === 0) {
+ this._radiusDown = [e.clientX, e.clientY];
+ this._isPointerDown = true;
+ this._resizeUndo = UndoManager.StartBatch("DocDecs set radius");
+ document.removeEventListener("pointermove", this.onRadiusMove);
+ document.removeEventListener("pointerup", this.onRadiusUp);
+ document.addEventListener("pointermove", this.onRadiusMove);
+ document.addEventListener("pointerup", this.onRadiusUp);
+ }
+ }
+
+ onRadiusMove = (e: PointerEvent): void => {
+ 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 => Doc.GetProto(dv.props.Document).borderRounding = `${Math.min(100, dist)}%`);
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ onRadiusUp = (e: PointerEvent): void => {
+ e.stopPropagation();
+ e.preventDefault();
+ this._isPointerDown = false;
+ this._resizeUndo && this._resizeUndo.end();
+ document.removeEventListener("pointermove", this.onRadiusMove);
+ document.removeEventListener("pointerup", this.onRadiusUp);
+ }
+
@action
onPointerDown = (e: React.PointerEvent): void => {
e.stopPropagation();
@@ -705,6 +736,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
<div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
<div id="documentDecorations-bottomResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
<div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id="documentDecorations-borderRadius" className="documentDecorations-radius" onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}><span className="borderRadiusTooltip" title="Drag Corner Radius"></span></div>
<div className="link-button-container">
<div className="linkButtonWrapper">
<div title="View Links" className="linkFlyout" ref={this._linkButton}> {linkButton} </div>
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index ea6f48d0f..989fb1be9 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -25,6 +25,7 @@ export interface EditableProps {
*/
contents: any;
fontStyle?: string;
+ fontSize?: number;
height?: number;
display?: string;
oneLine?: boolean;
@@ -85,7 +86,7 @@ export class EditableView extends React.Component<EditableProps> {
if (this._editing) {
return <input className="editableView-input" defaultValue={this.props.GetValue()} onKeyDown={this.onKeyDown} autoFocus
onBlur={action(() => this._editing = false)} onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation}
- style={{ display: this.props.display }} />;
+ style={{ display: this.props.display, fontSize: this.props.fontSize }} />;
} else {
return (
<div className={`editableView-container-editing${this.props.oneLine ? "-oneLine" : ""}`}
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 0461d7299..c7f7bdb66 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -10,6 +10,8 @@ import { InkTool } from "../../new_fields/InkField";
import { Doc } from "../../new_fields/Doc";
import { undoBatch, UndoManager } from "../util/UndoManager";
import { StrCast } from "../../new_fields/Types";
+import { FormattedTextBox } from "./nodes/FormattedTextBox";
+import { MainOverlayTextBox } from "./MainOverlayTextBox";
library.add(faPen, faHighlighter, faEraser, faBan);
@@ -42,6 +44,7 @@ export class InkingControl extends React.Component {
switchColor = action((color: ColorResult): void => {
this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff");
if (InkingControl.Instance.selectedTool === InkTool.None) {
+ if (MainOverlayTextBox.Instance.SetColor(color.hex)) return;
let selected = SelectionManager.SelectedDocuments();
let oldColors = selected.map(view => {
let targetDoc = view.props.Document.isTemplate ? view.props.Document : Doc.GetProto(view.props.Document);
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 08f5cb8a5..f52e3b658 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -111,8 +111,8 @@ button:hover {
//toolbar stuff
#toolbar {
position: absolute;
- bottom: 62px;
- left: 24px;
+ right: 8px;
+ top: 5px;
.toolbar-button {
display: block;
@@ -144,7 +144,7 @@ button:hover {
#add-nodes-menu {
position: absolute;
bottom: 22px;
- left: 24px;
+ left: 250px;
> label {
background: $dark-color;
@@ -230,6 +230,8 @@ ul#add-options-list {
.mainView-libraryFlyout {
height: 100%;
position: absolute;
+ display: flex;
+ flex-direction:column;
}
.mainView-libraryHandle {
@@ -241,4 +243,20 @@ ul#add-options-list {
position: absolute;
z-index: 1;
background: gray;
+}
+
+.mainView-workspace {
+ height:200px;
+ position:relative;
+ display:flex;
+}
+.mainView-library {
+ height:75%;
+ position:relative;
+ display:flex;
+}
+.mainView-recentlyClosed {
+ height:25%;
+ position:relative;
+ display:flex;
} \ No newline at end of file
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index d31319429..d8aaea259 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -30,11 +30,17 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
private _textBox: FormattedTextBox | undefined;
private _tooltip?: HTMLElement;
@observable public TextDoc?: Doc;
+ @observable public TextDataDoc?: Doc;
updateTooltip = () => {
this._outerdiv && this._tooltip && !this._outerdiv.contains(this._tooltip) && this._outerdiv.appendChild(this._tooltip);
}
+ public SetColor(color: string) {
+ return this._textBox && this._textBox.setFontColor(color);
+ }
+
+
constructor(props: MainOverlayTextBoxProps) {
super(props);
this._textProxyDiv = React.createRef();
@@ -43,13 +49,15 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
(box?: FormattedTextBox) => {
this._textBox = box;
if (box) {
- this.TextDoc = box.props.DataDoc;
+ this.TextDoc = box.props.Document;
+ this.TextDataDoc = box.props.DataDoc;
let sxf = Utils.GetScreenTransform(box ? box.CurrentDiv : undefined);
let xf = () => { box.props.ScreenToLocalTransform(); return new Transform(-sxf.translateX, -sxf.translateY, 1 / sxf.scale); };
this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf, BoolCast(box.props.Document.autoHeight, false) || box.props.height === "min-content");
}
else {
this.TextDoc = undefined;
+ this.TextDataDoc = undefined;
this.setTextDoc();
}
});
@@ -114,7 +122,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
}
}
render() {
- this.TextDoc;
+ this.TextDoc; this.TextDataDoc;
if (FormattedTextBox.InputBoxOverlay && this._textTargetDiv) {
let textRect = this._textTargetDiv.getBoundingClientRect();
let s = this._textXf().Scale;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 60decff25..b3074ac30 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,43 +1,43 @@
import { IconName, library } from '@fortawesome/fontawesome-svg-core';
-import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faArrowDown, faArrowUp, faCheck, faPenNib, faThumbtack, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt, faCut, faExclamation } from '@fortawesome/free-solid-svg-icons';
+import { faArrowDown, faArrowUp, faBell, faCheck, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faThumbtack, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, configure, observable, runInAction, trace } from 'mobx';
+import { action, computed, configure, observable, runInAction, reaction, trace } from 'mobx';
import { observer } from 'mobx-react';
-import { CirclePicker, SliderPicker, BlockPicker, TwitterPicker, SketchPicker } from 'react-color';
import "normalize.css";
import * as React from 'react';
+import { SketchPicker } from 'react-color';
import Measure from 'react-measure';
import * as request from 'request';
+import { Doc, DocListCast, Opt, HeightSym } from '../../new_fields/Doc';
+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 } from '../../new_fields/Types';
import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
import { RouteStore } from '../../server/RouteStore';
-import { emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils';
-import { Docs, DocTypes } from '../documents/Documents';
-import { SetupDrag, DragManager } from '../util/DragManager';
+import { emptyFunction, returnOne, returnTrue } from '../../Utils';
+import { DocServer } from '../DocServer';
+import { Docs } from '../documents/Documents';
+import { SetupDrag } from '../util/DragManager';
+import { HistoryUtil } from '../util/History';
import { Transform } from '../util/Transform';
import { UndoManager } from '../util/UndoManager';
-import { PresentationView } from './presentationview/PresentationView';
+import { CollectionBaseView } from './collections/CollectionBaseView';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { ContextMenu } from './ContextMenu';
import { DocumentDecorations } from './DocumentDecorations';
+import KeyManager from './GlobalKeyHandler';
import { InkingControl } from './InkingControl';
import "./Main.scss";
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 { SelectionManager } from '../util/SelectionManager';
-import { FieldResult, Field, Doc, Opt, DocListCast } from '../../new_fields/Doc';
-import { Cast, FieldValue, StrCast, PromiseValue } from '../../new_fields/Types';
-import { DocServer } from '../DocServer';
-import { listSpec } from '../../new_fields/Schema';
-import { Id } from '../../new_fields/FieldSymbols';
-import { HistoryUtil } from '../util/History';
-import { CollectionBaseView } from './collections/CollectionBaseView';
-import { List } from '../../new_fields/List';
-import PDFMenu from './pdf/PDFMenu';
-import { InkTool } from '../../new_fields/InkField';
-import _ from "lodash";
-import KeyManager from './GlobalKeyHandler';
+import { CollectionTreeView } from './collections/CollectionTreeView';
@observer
export class MainView extends React.Component {
@@ -72,6 +72,21 @@ export class MainView extends React.Component {
window.removeEventListener("pointerup", this.pointerUp);
window.addEventListener("pointerup", this.pointerUp);
+
+ reaction(() => {
+ let workspaces = CurrentUserUtils.UserDocument.workspaces;
+ let recent = CurrentUserUtils.UserDocument.recentlyClosed;
+ if (!(recent instanceof Doc)) return 0;
+ if (!(workspaces instanceof Doc)) return 0;
+ let workspacesDoc = workspaces;
+ let recentDoc = recent;
+ let libraryHeight = this.getPHeight() - workspacesDoc[HeightSym]() - recentDoc[HeightSym]() - 20 + CurrentUserUtils.UserDocument[HeightSym]() * 0.00001;
+ return libraryHeight;
+ }, (libraryHeight: number) => {
+ if (libraryHeight && Math.abs(CurrentUserUtils.UserDocument[HeightSym]() - libraryHeight) > 5) {
+ CurrentUserUtils.UserDocument.height = libraryHeight;
+ }
+ }, { fireImmediately: true });
}
pointerDown = (e: PointerEvent) => this.isPointerDown = true;
@@ -163,7 +178,9 @@ export class MainView extends React.Component {
@action
createNewWorkspace = async (id?: string) => {
- const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc));
+ let workspaces = Cast(CurrentUserUtils.UserDocument.workspaces, Doc);
+ if (!(workspaces instanceof Doc)) return;
+ const list = Cast((CurrentUserUtils.UserDocument.workspaces as Doc).data, listSpec(Doc));
if (list) {
let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, width: this.pwidth * .7, height: this.pheight, title: `WS collection ${list.length + 1}` });
var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] };
@@ -183,8 +200,6 @@ export class MainView extends React.Component {
}
}
- @observable _notifsCol: Opt<Doc>;
-
@action
openWorkspace = async (doc: Doc, fromHistory = false) => {
CurrentUserUtils.MainDocId = doc[Id];
@@ -196,18 +211,12 @@ export class MainView extends React.Component {
if (col) {
const l = Cast(col.data, listSpec(Doc));
if (l) {
- runInAction(() => this._notifsCol = col);
+ runInAction(() => CollectionTreeView.NotifsCol = col);
}
}
}, 100);
}
- openNotifsCol = () => {
- if (this._notifsCol && CollectionDockingView.Instance) {
- CollectionDockingView.Instance.AddRightSplit(this._notifsCol, undefined);
- }
- }
-
onDrop = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
@@ -283,24 +292,28 @@ export class MainView extends React.Component {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
}
+ flyoutWidthFunc = () => this.flyoutWidth;
+ addDocTabFunc = (doc: Doc) => {
+ if (doc.dockingConfig) {
+ this.openWorkspace(doc);
+ } else {
+ CollectionDockingView.Instance.AddRightSplit(doc, undefined);
+ }
+ };
@computed
- get mainContent() {
- let addDocTab = (doc: Doc, dataDoc: Doc | undefined, location: string) => {
- if (doc.dockingConfig) {
- this.openWorkspace(doc);
- } else {
- CollectionDockingView.Instance.AddRightSplit(doc, dataDoc);
- }
- };
- let flyout = <DocumentView
- Document={CurrentUserUtils.UserDocument}
+ get flyout() {
+ let sidebar = CurrentUserUtils.UserDocument.sidebar;
+ if (!(sidebar instanceof Doc)) return (null);
+ let sidebarDoc = sidebar;
+ return <DocumentView
+ Document={sidebarDoc}
DataDoc={undefined}
addDocument={undefined}
- addDocTab={(doc: Doc) => addDocTab(doc, undefined, "onRight")}
+ addDocTab={this.addDocTabFunc}
removeDocument={undefined}
ScreenToLocalTransform={Transform.Identity}
ContentScaling={returnOne}
- PanelWidth={this.getPWidth}
+ PanelWidth={this.flyoutWidthFunc}
PanelHeight={this.getPHeight}
renderDepth={0}
selectOnLoad={false}
@@ -311,7 +324,10 @@ export class MainView extends React.Component {
ContainingCollectionView={undefined}
zoomToScale={emptyFunction}
getScale={returnOne}>
- </DocumentView>;
+ </DocumentView>
+ }
+ @computed
+ get mainContent() {
return <div>
<div className="mainView-libraryHandle"
style={{ left: `${this.flyoutWidth - 10}px` }}
@@ -319,7 +335,7 @@ export class MainView extends React.Component {
<span title="library View Dragger" style={{ width: "100%", height: "100%", position: "absolute" }} />
</div>
<div className="mainView-libraryFlyout" style={{ width: `${this.flyoutWidth}px` }}>
- {flyout}
+ {this.flyout}
</div>
{this.dockingContent}
</div>;
@@ -360,7 +376,7 @@ export class MainView extends React.Component {
[React.createRef<HTMLDivElement>(), "arrow-up", "Import Directory", addImportCollectionNode],
];
- return < div id="add-nodes-menu" >
+ return < div id="add-nodes-menu" style={{ left: this.flyoutWidth + 5 }} >
<input type="checkbox" id="add-menu-toggle" ref={this.addMenuToggle} />
<label htmlFor="add-menu-toggle" title="Add Node"><p>+</p></label>
@@ -401,23 +417,9 @@ export class MainView extends React.Component {
/* @TODO this should really be moved into a moveable toolbar component, but for now let's put it here to meet the deadline */
@computed
get miscButtons() {
- const length = this._notifsCol ? DocListCast(this._notifsCol.data).length : 0;
- const notifsRef = React.createRef<HTMLDivElement>();
- const dragNotifs = action(() => this._notifsCol!);
let logoutRef = React.createRef<HTMLDivElement>();
return [
- <div id="toolbar" key="toolbar">
- <div ref={notifsRef}>
- <button className="toolbar-button round-button" title="Notifs"
- onClick={this.openNotifsCol} onPointerDown={this._notifsCol ? SetupDrag(notifsRef, dragNotifs) : emptyFunction}>
- <FontAwesomeIcon icon={faBell} size="sm" />
- </button>
- <div className="main-notifs-badge" style={length > 0 ? { "display": "initial" } : { "display": "none" }}>
- {length}
- </div>
- </div>
- </div >,
this.isSearchVisible ? <div className="main-searchDiv" key="search" style={{ top: '34px', right: '1px', position: 'absolute' }} > <FilterBox /> </div> : null,
<div className="main-buttonDiv" key="logout" style={{ bottom: '0px', right: '1px', position: 'absolute' }} ref={logoutRef}>
<button onClick={() => request.get(DocServer.prepend(RouteStore.logout), emptyFunction)}>Log Out</button></div>
@@ -443,6 +445,7 @@ export class MainView extends React.Component {
{this.miscButtons}
<PDFMenu />
<MainOverlayTextBox />
+ <OverlayView />
</div>
);
}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
new file mode 100644
index 000000000..f8fc94274
--- /dev/null
+++ b/src/client/views/OverlayView.tsx
@@ -0,0 +1,47 @@
+import * as React from "react";
+import { observer } from "mobx-react";
+import { observable, action } from "mobx";
+import { Utils } from "../../Utils";
+
+export type OverlayDisposer = () => void;
+
+export type OverlayElementOptions = {
+ x: number;
+ y: number;
+ width?: number;
+ height?: number;
+};
+
+@observer
+export class OverlayView extends React.Component {
+ public static Instance: OverlayView;
+ @observable.shallow
+ private _elements: { ele: JSX.Element, id: string, options: OverlayElementOptions }[] = [];
+
+ constructor(props: any) {
+ super(props);
+ if (!OverlayView.Instance) {
+ OverlayView.Instance = this;
+ }
+ }
+
+ @action
+ addElement(ele: JSX.Element, options: OverlayElementOptions): OverlayDisposer {
+ const eleWithPosition = { ele, options, id: Utils.GenerateGuid() };
+ this._elements.push(eleWithPosition);
+ return action(() => {
+ const index = this._elements.indexOf(eleWithPosition);
+ if (index !== -1) this._elements.splice(index, 1);
+ });
+ }
+
+ render() {
+ return (
+ <div>
+ {this._elements.map(({ ele, options: { x, y, width, height }, id }) => (
+ <div key={id} style={{ position: "absolute", transform: `translate(${x}px, ${y}px)`, width, height }}>{ele}</div>
+ ))}
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/ScriptBox.scss b/src/client/views/ScriptBox.scss
new file mode 100644
index 000000000..28326624a
--- /dev/null
+++ b/src/client/views/ScriptBox.scss
@@ -0,0 +1,17 @@
+.scriptBox-outerDiv {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.scriptBox-toolbar {
+ width: 100%;
+}
+
+.scriptBox-textArea {
+ width: 100%;
+ height: 100%;
+ box-sizing: border-box;
+ resize: none;
+} \ No newline at end of file
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
new file mode 100644
index 000000000..fa236c2da
--- /dev/null
+++ b/src/client/views/ScriptBox.tsx
@@ -0,0 +1,44 @@
+import * as React from "react";
+import { observer } from "mobx-react";
+import { observable, action } from "mobx";
+
+import "./ScriptBox.scss";
+
+export interface ScriptBoxProps {
+ onSave: (text: string, onError: (error: string) => void) => void;
+ onCancel?: () => void;
+ initialText?: string;
+}
+
+@observer
+export class ScriptBox extends React.Component<ScriptBoxProps> {
+ @observable
+ private _scriptText: string;
+
+ constructor(props: ScriptBoxProps) {
+ super(props);
+ this._scriptText = props.initialText || "";
+ }
+
+ @action
+ onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
+ this._scriptText = e.target.value;
+ }
+
+ @action
+ onError = (error: string) => {
+ console.log(error);
+ }
+
+ render() {
+ return (
+ <div className="scriptBox-outerDiv">
+ <div className="scriptBox-toolbar">
+ <button onClick={e => { this.props.onSave(this._scriptText, this.onError); e.stopPropagation(); }}>Save</button>
+ <button onClick={e => { this.props.onCancel && this.props.onCancel(); e.stopPropagation(); }}>Cancel</button>
+ </div>
+ <textarea className="scriptBox-textarea" onChange={this.onChange} value={this._scriptText}></textarea>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 4843a70a6..236704fa2 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -69,8 +69,7 @@ export namespace Templates {
`< div >
<div style="height:100%; width:100%;position:absolute;">{layout}</div>
<div id="isExpander" style="height:15px; width:15px; margin-left:-16px; pointer-events:all; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white;">
- <img id="isExpander" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAZlBMVEX///8AAABmZmb7+/tYWFhgYGBFRUVSUlL4+Pg/Pz9jY2N5eXmcnJyioqKBgYFzc3NtbW1LS0s3NzfW1taWlpaOjo6IiIgvLy9WVlampqZcXFw5OTlvb28mJiYxMTHe3t7l5eUjIyMY8kIZAAAD2UlEQVR4nO2d61biMBRGW1FBEVHxfp15/5ecOVa5lHxtArmck/Xtn1BotjtNoXQtm4YQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEFIrX6UHEA1gsmrneceRjHm7cj28attKFOf/TRyKIliH4vzbZE+xE2zbZYkxRWX5Y9JT/BW0X3G+NtlR3Ahar7jcMtlS3Ba0XXG+Y7JW3BW0XHHZM/lR7AvaVewL/ijuC1pV3Bf8VnQJ2lR0CYriq/Nxg4puwfa1aZ7dz9yUHnEgN26NZ3luWkPFd7fEtHsWVDwpO+YgTgYKCuYn6tAU7TBecaygcGpZEQie7m5luKJPQQFUvCwx5iAuvQoK4KShvSIoOHVtCz7dnOUecxBn7kG/urc2eCz6T9EOcxXDCgpAUetyAwoOCBqrGF5QMKR4mCA8L+pTBIJwkRl95eifJjPHTDYTFQ8vePyrs3BsBfXLzfFHkvKKMY4j1ctNnCmmuGKslfCQT0RZiPdFVmnFmOcy36sDWYn7DU9hxdifRkKuEGQh/pWW0K/QiUlxtUxVxTTXyhQtN6kuI6mpmO5qpxJFIBjl1yMVimmvV4PfrnIq3iYsKICTRj7F9L84gIq38fYwCCj4HnMfRY/FPL8ZFayYo6BQbLlJeZrYpVDFXAUFcMtKWkUgmOhmnwKKOQsK4NaxdIp5CwqZj8X8gv27jNecJ9nZuXtnie/SzjhRQcHkt6Fnq1imoAAUY1csVVDIUrFcQSGDIhC8jriLQZIrli0oXKdVLF1QSFqxfEEBVLyI8NYXCgoKySaqhinakajimxrBRBX1FBQSVNRyDP4SXVGbYHRFfYJN8xhTESwyj5HHHEjEihoLCqDiXfAb3aksKESqCAoqEIxUUW9BAS03E+93mOhcZDYcXVF3QeHBPcI3v4qo4EPiUQcBKr75vHaiv6AAKt6NV0SCqgoKqOKYovpFZgOo+DmsOHkyUlA4ZKKamaIdQPEJK5oqKKCKM7D9zFZBIayiuYICWm5cFWef7o3vs486CP8VdQIEVRcU7sFE7VecgSmqvKDgVxEJqi8ogIof2xVnH2YLCuMT1fAU7RirOPtrXHCsovmCwlDFCgoKWNH4IrMBTdQ/NUzRjiu3CeCq9HAPAVSspaDgX9FkQcG3ollB34qGBf0UTQv6KBoXHFc0LzimWIFg0ywGBBelBxcHXLGKggKqWElBwV2xIkF3xaoEXYqVCe4rVifYV3wpPZwULOouKLzUXVBY1F1QeKm7oLCoXVAqVi7YNM7/F0YIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCG+/ANh4i1CHdc63QAAAABJRU5ErkJggg=="
- width="15px" height="15px" />
+ <img id="isExpander" src="/assets/downarrow.png" width="15px" height="15px" />
</div>
</div > `
);
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 879898018..e4f9b5058 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -64,7 +64,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
active = (): boolean => {
var isSelected = this.props.isSelected();
- return isSelected || this._isChildActive || this.props.renderDepth === 0;
+ return isSelected || this._isChildActive || this.props.renderDepth === 0 || BoolCast(this.props.Document.excludeFromLibrary);
}
//TODO should this be observable?
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 8724216f5..e0270fab3 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -27,6 +27,7 @@ import { MainView } from '../MainView';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
import { faFile } from '@fortawesome/free-solid-svg-icons';
+import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
library.add(faFile);
@observer
@@ -240,6 +241,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
// Because this is in a set timeout, if this component unmounts right after mounting,
// we will leak a GoldenLayout, because we try to destroy it before we ever create it
setTimeout(() => this.setupGoldenLayout(), 1);
+ this.props.Document.workspaceBrush = true;
}
this._ignoreStateChange = "";
}, { fireImmediately: true });
@@ -249,6 +251,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
componentWillUnmount: () => void = () => {
try {
+ this.props.Document.workspaceBrush = false;
this._goldenLayout.unbind('itemDropped', this.itemDropped);
this._goldenLayout.unbind('tabCreated', this.tabCreated);
this._goldenLayout.unbind('stackCreated', this.stackCreated);
@@ -414,6 +417,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
if (doc instanceof Doc) {
let theDoc = doc;
CollectionDockingView.Instance._removedDocs.push(theDoc);
+ if (CurrentUserUtils.UserDocument.recentlyClosed instanceof Doc) {
+ Doc.AddDocToList(CurrentUserUtils.UserDocument.recentlyClosed, "data", doc, undefined, true, true);
+ }
SelectionManager.DeselectAll();
}
tab.contentItem.remove();
@@ -455,6 +461,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
render() {
+ if (this.props.renderDepth > 0) {
+ return <div style={{ width: "100%", height: "100%" }}>Nested workspaces can't be rendered</div>;
+ }
return (
<Measure offset onResize={this.onResize}>
{({ measureRef }) =>
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index b0d46953c..568949efb 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -334,7 +334,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@computed
get reactTable() {
- trace();
let previewWidth = this.previewWidth() + 2 * this.borderWidth + this.DIVIDER_WIDTH + 1;
return <ReactTable style={{ position: "relative", float: "left", width: `calc(100% - ${previewWidth}px` }} data={this.childDocs} page={0} pageSize={this.childDocs.length} showPagination={false}
columns={this.tableColumns}
@@ -388,7 +387,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
render() {
- trace();
return (
<div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel}
onDrop={(e: React.DragEvent) => this.onDrop(e, {})} onContextMenu={this.onContextMenu} ref={this.createTarget}>
@@ -404,6 +402,7 @@ interface CollectionSchemaPreviewProps {
Document?: Doc;
DataDocument?: Doc;
childDocs?: Doc[];
+ fitToBox?: () => number[];
renderDepth: number;
width: () => number;
height: () => number;
@@ -462,16 +461,32 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.props.setPreviewScript(e.currentTarget.value);
}
+ @computed get borderRounding() {
+ let br = StrCast(this.props.Document!.borderRounding);
+ if (br.endsWith("%")) {
+ let percent = Number(br.substr(0, br.length - 1)) / 100;
+ let nativeDim = Math.min(NumCast(this.props.Document!.nativeWidth), NumCast(this.props.Document!.nativeHeight));
+ let minDim = percent * (nativeDim ? nativeDim : Math.min(this.PanelWidth(), this.PanelHeight()));
+ return minDim;
+ }
+ return undefined;
+ }
render() {
let input = this.props.previewScript === undefined ? (null) :
<div ref={this.createTarget}><input className="collectionSchemaView-input" value={this.props.previewScript} onChange={this.onPreviewScriptChange}
style={{ left: `calc(50% - ${Math.min(75, (this.props.Document ? this.PanelWidth() / 2 : 75))}px)` }} /></div>;
return (<div className="collectionSchemaView-previewRegion" style={{ width: this.props.width(), height: "100%" }}>
{!this.props.Document || !this.props.width ? (null) : (
- <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.centeringOffset}px, 0px)`, height: "100%" }}>
+ <div className="collectionSchemaView-previewDoc"
+ style={{
+ transform: `translate(${this.centeringOffset}px, 0px)`,
+ borderRadius: this.borderRounding,
+ height: "100%"
+ }}>
<DocumentView
DataDoc={this.props.Document.layout instanceof Doc ? this.props.Document : this.props.DataDocument}
Document={this.props.Document}
+ fitToBox={this.props.fitToBox}
renderDepth={this.props.renderDepth + 1}
selectOnLoad={false}
addDocument={this.props.addDocument}
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 034a09eaa..bc733f152 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -16,7 +16,7 @@
align-items: center;
}
- .collectionStackingView-masonrySingle, .collectionStackingView-masonryGrid{
+ .collectionStackingView-masonrySingle, .collectionStackingView-masonryGrid {
width:100%;
height:100%;
position: absolute;
@@ -25,7 +25,17 @@
left: 0;
width: 100%;
position: absolute;
-
+ }
+ .collectionStackingView-masonrySingle {
+ width:100%;
+ height:100%;
+ position: absolute;
+ display:flex;
+ flex-direction: column;
+ top: 0;
+ left: 0;
+ width: 100%;
+ position: absolute;
}
.collectionStackingView-description {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index b10907937..aea74321e 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -1,6 +1,6 @@
import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, reaction } from "mobx";
+import { action, computed, IReactionDisposer, reaction, untracked } from "mobx";
import { observer } from "mobx-react";
import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
@@ -10,7 +10,8 @@ import { ContextMenu } from "../ContextMenu";
import { CollectionSchemaPreview } from "./CollectionSchemaView";
import "./CollectionStackingView.scss";
import { CollectionSubView } from "./CollectionSubView";
-import { resolve } from "bluebird";
+import { undoBatch } from "../../util/UndoManager";
+import { DragManager } from "../../util/DragManager";
@observer
export class CollectionStackingView extends CollectionSubView(doc => doc) {
@@ -23,6 +24,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@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); }
singleColDocHeight(d: Doc) {
let nw = NumCast(d.nativeWidth);
@@ -33,14 +35,10 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
componentDidMount() {
this._heightDisposer = reaction(() => [this.yMargin, this.gridGap, this.columnWidth, this.childDocs.map(d => [d.height, d.width, d.zoomBasis, d.nativeHeight, d.nativeWidth, d.isMinimized])],
- () => {
- if (this.singleColumn) {
- let children = this.childDocs.filter(d => !d.isMinimized);
- this.props.Document.height = children.reduce((height, d, i) =>
- height + this.singleColDocHeight(d) + (i === children.length - 1 ? this.yMargin : this.gridGap)
- , this.yMargin);
- }
- }, { fireImmediately: true });
+ () => this.singleColumn &&
+ (this.props.Document.height = this.filteredChildren.reduce((height, d, i) =>
+ height + this.singleColDocHeight(d) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap), this.yMargin))
+ , { fireImmediately: true });
}
componentWillUnmount() {
if (this._heightDisposer) this._heightDisposer();
@@ -48,14 +46,14 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@action
moveDocument = (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean): boolean => {
- this.props.removeDocument(doc);
- addDocument(doc);
- return true;
+ return this.props.removeDocument(doc) && addDocument(doc);
}
- getDocTransform(doc: Doc, dref: HTMLDivElement) {
- let { scale, translateX, translateY } = Utils.GetScreenTransform(dref);
+ getSingleDocTransform(doc: Doc, ind: number, width: number) {
+ let localY = this.filteredChildren.reduce((height, d, i) =>
+ height + (i < ind ? this.singleColDocHeight(d) + this.gridGap : 0), this.yMargin);
+ let translate = this.props.ScreenToLocalTransform().inverse().transformPoint((this.props.PanelWidth() - width) / 2, localY);
let outerXf = Utils.GetScreenTransform(this._masonryGridRef!);
- let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
+ let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translate[0], outerXf.translateY - translate[1]);
return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]).scale(NumCast(doc.width, 1) / this.columnWidth);
}
createRef = (ele: HTMLDivElement | null) => {
@@ -65,17 +63,15 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@computed
get singleColumnChildren() {
- let children = this.childDocs.filter(d => !d.isMinimized);
- return children.map((d, i) => {
+ return this.filteredChildren.map((d, i) => {
let layoutDoc = Doc.expandTemplateLayout(d, this.props.DataDoc);
- let dref = React.createRef<HTMLDivElement>();
- let dxf = () => this.getDocTransform(layoutDoc, dref.current!).scale(this.columnWidth / d[WidthSym]());
let width = () => d.nativeWidth ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth;
let height = () => this.singleColDocHeight(layoutDoc);
+ let dxf = () => this.getSingleDocTransform(layoutDoc, i, width()).scale(this.columnWidth / d[WidthSym]());
+ let gap = i === 0 ? 0 : this.gridGap;
return <div className="collectionStackingView-columnDoc"
key={d[Id]}
- ref={dref}
- style={{ width: width(), height: height() }} >
+ style={{ width: width(), display: "inline-block", marginTop: gap, height: `${height() / (this.props.Document[HeightSym]() - 2 * this.yMargin) * 100}%` }} >
<CollectionSchemaPreview
Document={layoutDoc}
DataDocument={d !== this.props.DataDoc ? this.props.DataDoc : undefined}
@@ -96,15 +92,24 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
</div>;
});
}
+ 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);
+ }
+ docXfs: any[] = []
@computed
get children() {
- return this.childDocs.filter(d => !d.isMinimized).map((d, i) => {
+ this.docXfs.length = 0;
+ return this.filteredChildren.map((d, i) => {
let aspect = d.nativeHeight ? NumCast(d.nativeWidth) / NumCast(d.nativeHeight) : undefined;
let dref = React.createRef<HTMLDivElement>();
let dxf = () => this.getDocTransform(d, dref.current!).scale(this.columnWidth / d[WidthSym]());
let width = () => d.nativeWidth ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth;
let height = () => aspect ? width() / aspect : d[HeightSym]();
let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + this.gridGap));
+ this.docXfs.push({ dxf: dxf, width: width, height: height });
return (<div className="collectionStackingView-masonryDoc"
key={d[Id]}
ref={dref}
@@ -166,13 +171,64 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
});
}
}
+
+ @undoBatch
+ @action
+ drop = (e: Event, de: DragManager.DropEvent) => {
+ let targInd = -1;
+ let where = [de.x, de.y];
+ if (de.data instanceof DragManager.DocumentDragData) {
+ this.docXfs.map((cd, i) => {
+ let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
+ let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
+ if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
+ targInd = i;
+ }
+ })
+ }
+ if (super.drop(e, de)) {
+ let newDoc = de.data.droppedDocuments[0];
+ let docs = this.childDocList;
+ if (docs) {
+ if (targInd === -1) targInd = docs.length;
+ else targInd = docs.indexOf(this.filteredChildren[targInd]);
+ let srcInd = docs.indexOf(newDoc);
+ docs.splice(srcInd, 1);
+ docs.splice(targInd > srcInd ? targInd - 1 : targInd, 0, newDoc);
+ }
+ }
+ return false;
+ }
+ @undoBatch
+ @action
+ onDrop = (e: React.DragEvent): void => {
+ let where = [e.clientX, e.clientY];
+ let targInd = -1;
+ this.docXfs.map((cd, i) => {
+ let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
+ let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
+ if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
+ targInd = i;
+ }
+ })
+ super.onDrop(e, {}, () => {
+ if (targInd !== -1) {
+ let newDoc = this.childDocs[this.childDocs.length - 1];
+ let docs = this.childDocList;
+ if (docs) {
+ docs.splice(docs.length - 1, 1);
+ docs.splice(targInd, 0, newDoc);
+ }
+ }
+ });
+ }
render() {
- let cols = this.singleColumn ? 1 : Math.max(1, Math.min(this.childDocs.filter(d => !d.isMinimized).length,
+ let cols = this.singleColumn ? 1 : Math.max(1, Math.min(this.filteredChildren.length,
Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
let templatecols = "";
for (let i = 0; i < cols; i++) templatecols += `${this.columnWidth}px `;
return (
- <div className="collectionStackingView" ref={this.createRef} onContextMenu={this.onContextMenu} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
+ <div className="collectionStackingView" ref={this.createRef} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
<div className={`collectionStackingView-masonry${this.singleColumn ? "Single" : "Grid"}`}
style={{
padding: this.singleColumn ? `${this.yMargin}px ${this.xMargin}px ${this.yMargin}px ${this.xMargin}px` : `${this.yMargin}px ${this.xMargin}px`,
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 8cb2e1beb..bf17088ae 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -53,6 +53,11 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
//This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue)
return DocListCast((BoolCast(this.props.Document.isTemplate) ? this.extensionDoc : this.props.Document)[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey]);
}
+ get childDocList() {
+ //TODO tfs: This might not be what we want?
+ //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue)
+ return Cast((BoolCast(this.props.Document.isTemplate) ? this.extensionDoc : this.props.Document)[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey], listSpec(Doc));
+ }
@action
protected async setCursorPosition(position: [number, number]) {
@@ -97,6 +102,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
return added;
}
else if (de.data instanceof DragManager.AnnotationDragData) {
+ e.stopPropagation();
return this.props.addDocument(de.data.dropDocument);
}
return false;
@@ -104,7 +110,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
@undoBatch
@action
- protected onDrop(e: React.DragEvent, options: DocumentOptions): void {
+ protected onDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) {
if (e.ctrlKey) {
e.stopPropagation(); // bcz: this is a hack to stop propagation when dropping an image on a text document with shift+ctrl
return;
@@ -213,7 +219,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
}
if (promises.length) {
- Promise.all(promises).finally(() => batch.end());
+ Promise.all(promises).finally(() => { completed && completed(); batch.end(); });
} else {
batch.end();
}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index f5dd76b71..c83a2d2c6 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,15 +1,15 @@
import { library } from '@fortawesome/fontawesome-svg-core';
-import { faAngleRight, faCaretDown, faCaretRight, faCaretSquareDown, faCaretSquareRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
+import { faAngleRight, faCamera, faExpand, faBell, faCaretDown, faCaretRight, faCaretSquareDown, faCaretSquareRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from "mobx";
+import { action, computed, observable, trace } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc';
+import { Doc, DocListCast, HeightSym, WidthSym, Opt } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
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 } from '../../documents/Documents';
+import { Docs, DocUtils, DocTypes } from '../../documents/Documents';
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager";
import { SelectionManager } from '../../util/SelectionManager';
@@ -50,6 +50,9 @@ export interface TreeViewProps {
library.add(faTrashAlt);
library.add(faAngleRight);
+library.add(faBell);
+library.add(faCamera);
+library.add(faExpand);
library.add(faCaretDown);
library.add(faCaretRight);
library.add(faCaretSquareDown);
@@ -65,6 +68,7 @@ class TreeView extends React.Component<TreeViewProps> {
private _dref = React.createRef<HTMLDivElement>();
@observable __chosenKey: string = "";
@computed get _chosenKey() { return this.__chosenKey ? this.__chosenKey : this.fieldKey; }
+ @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.document.maxEmbedHeight, 300); }
@observable _collapsed: boolean = true;
@computed get fieldKey() {
@@ -152,22 +156,15 @@ class TreeView extends React.Component<TreeViewProps> {
</div>;
}
- titleClicked = (e: React.MouseEvent) => {
- if (this._collapsed) return false;
- else {
- this.props.document.embed = !BoolCast(this.props.document.embed);
- return true;
- }
- }
static loadId = "";
editableView = (key: string, style?: string) => (<EditableView
oneLine={true}
display={"inline"}
editing={this.resolvedDataDoc[Id] === TreeView.loadId}
contents={StrCast(this.props.document[key])}
- onClick={this.titleClicked}
height={36}
fontStyle={style}
+ fontSize={12}
GetValue={() => StrCast(this.props.document[key])}
SetValue={(value: string) => (Doc.GetProto(this.resolvedDataDoc)[key] = value) ? true : true}
OnFillDown={(value: string) => {
@@ -221,7 +218,8 @@ class TreeView extends React.Component<TreeViewProps> {
return <>
<div className="docContainer" id={`docContainer-${this.props.parentKey}`} ref={reference} onPointerDown={onItemDown}
style={{
- background: BoolCast(this.props.document.protoBrush, false) ? "#06123232" : BoolCast(this.props.document.libraryBrush, false) ? "#06121212" : "0",
+ background: BoolCast(this.props.document.libraryBrush, false) ? "#06121212" : "0",
+ outline: BoolCast(this.props.document.workspaceBrush, false) ? "dashed 1px #06123232" : undefined,
pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? "all" : "none"
}}
>
@@ -235,20 +233,22 @@ class TreeView extends React.Component<TreeViewProps> {
onWorkspaceContextMenu = (e: React.MouseEvent): void => {
if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
- ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.resolvedDataDoc)) });
- ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
+ ContextMenu.Instance.addItem({ description: (BoolCast(this.props.document.embed) ? "Collapse" : "Expand") + " inline", event: () => this.props.document.embed = !BoolCast(this.props.document.embed), icon: "expand" });
if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) {
ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "inTab"), icon: "folder" });
ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "onRight"), icon: "caret-square-right" });
if (DocumentManager.Instance.getDocumentViews(this.resolvedDataDoc).length) {
- ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.resolvedDataDoc).map(view => view.props.focus(this.props.document, true)) });
+ ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.resolvedDataDoc).map(view => view.props.focus(this.props.document, true)), icon: "camera" });
}
- ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)) });
+ ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)), icon: "trash-alt" });
} else {
- ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)) });
+ ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.resolvedDataDoc)), icon: "caret-square-right" });
+ ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)), icon: "trash-alt" });
}
+ ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15);
e.stopPropagation();
+ e.preventDefault();
}
}
@@ -266,6 +266,8 @@ class TreeView extends React.Component<TreeViewProps> {
e.stopPropagation();
}
if (de.data instanceof DragManager.DocumentDragData) {
+ e.stopPropagation();
+ if (de.data.draggedDocuments[0] === this.props.document) return true;
let addDoc = (doc: Doc) => this.props.addDocument(doc, this.resolvedDataDoc, before);
if (inside) {
let docList = Cast(this.resolvedDataDoc.data, listSpec(Doc));
@@ -273,7 +275,6 @@ class TreeView extends React.Component<TreeViewProps> {
addDoc = (doc: Doc) => { docList && docList.push(doc); return true; };
}
}
- e.stopPropagation();
let movedDocs = (de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments);
return (de.data.dropAction || de.data.userDropAction) ?
de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.resolvedDataDoc, before) || added, false)
@@ -312,13 +313,37 @@ class TreeView extends React.Component<TreeViewProps> {
return ele;
}
+ @computed get docBounds() {
+ if (StrCast(this.props.document.type).indexOf(DocTypes.COL) === -1) return undefined;
+ let layoutDoc = Doc.expandTemplateLayout(this.props.document, this.props.dataDoc);
+ return Doc.ComputeContentBounds(layoutDoc);
+ }
+ docWidth = () => {
+ let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth);
+ if (aspect) return Math.min(this.props.document[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 5));
+ return NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5;
+ }
+ docHeight = () => {
+ let bounds = this.docBounds;
+ return Math.min(this.MAX_EMBED_HEIGHT, (() => {
+ let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth);
+ if (aspect) return this.docWidth() * aspect;
+ if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x);
+ return NumCast(this.props.document.height) ? NumCast(this.props.document.height) : 50;
+ })());
+ }
+ fitToBox = () => {
+ let bounds = this.docBounds!;
+ return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Math.min(this.docHeight() / (bounds.b - bounds.y), this.docWidth() / (bounds.r - bounds.x))];
+ }
+
render() {
let contentElement: (JSX.Element | null) = null;
let docList = Cast(this.resolvedDataDoc[this._chosenKey], listSpec(Doc));
let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey);
let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.resolvedDataDoc, this._chosenKey, doc, addBefore, before);
let doc = Cast(this.resolvedDataDoc[this._chosenKey], Doc);
- let docWidth = () => NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5;
+
if (!this._collapsed) {
if (!this.props.document.embed) {
contentElement = <ul key={this._chosenKey + "more"}>
@@ -328,13 +353,14 @@ class TreeView extends React.Component<TreeViewProps> {
</ul >;
} else {
let layoutDoc = Doc.expandTemplateLayout(this.props.document, this.props.dataDoc);
- contentElement = <div ref={this._dref} style={{ display: "inline-block", height: layoutDoc[HeightSym]() }} key={this.props.document[Id]}>
+ contentElement = <div ref={this._dref} style={{ display: "inline-block", height: this.docHeight() }} key={this.props.document[Id]}>
<CollectionSchemaPreview
Document={layoutDoc}
DataDocument={this.resolvedDataDoc}
renderDepth={this.props.renderDepth}
- width={docWidth}
- height={layoutDoc[HeightSym]}
+ fitToBox={this.docBounds && !NumCast(this.props.document.nativeWidth) ? this.fitToBox : undefined}
+ width={this.docWidth}
+ height={this.docHeight}
getTransform={this.docTransform}
CollectionView={undefined}
addDocument={emptyFunction as any}
@@ -445,9 +471,12 @@ export class CollectionTreeView extends CollectionSubView(Document) {
}
onContextMenu = (e: React.MouseEvent): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
- if (!e.isPropagationStopped() && this.props.Document.excludeFromLibrary) { // excludeFromLibrary means this is the user document
+ if (!e.isPropagationStopped() && this.props.Document.workspaceLibrary) { // excludeFromLibrary means this is the user document
ContextMenu.Instance.addItem({ description: "Create Workspace", event: undoBatch(() => MainView.Instance.createNewWorkspace()) });
ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.remove(this.props.Document)) });
+ e.stopPropagation();
+ e.preventDefault();
+ ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
}
}
@@ -456,6 +485,31 @@ export class CollectionTreeView extends CollectionSubView(Document) {
outerXf = () => Utils.GetScreenTransform(this._mainEle!);
onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {});
+
+ @observable static NotifsCol: Opt<Doc>;
+
+ openNotifsCol = () => {
+ if (CollectionTreeView.NotifsCol && CollectionDockingView.Instance) {
+ CollectionDockingView.Instance.AddRightSplit(CollectionTreeView.NotifsCol, undefined);
+ }
+ }
+ @computed get notifsButton() {
+ const length = CollectionTreeView.NotifsCol ? DocListCast(CollectionTreeView.NotifsCol.data).length : 0;
+ const notifsRef = React.createRef<HTMLDivElement>();
+ const dragNotifs = action(() => CollectionTreeView.NotifsCol!);
+ return <div id="toolbar" key="toolbar">
+ <div ref={notifsRef}>
+ <button className="toolbar-button round-button" title="Notifs"
+ onClick={this.openNotifsCol} onPointerDown={CollectionTreeView.NotifsCol ? SetupDrag(notifsRef, dragNotifs) : emptyFunction}>
+ <FontAwesomeIcon icon={faBell} size="sm" />
+ </button>
+ <div className="main-notifs-badge" style={length > 0 ? { "display": "initial" } : { "display": "none" }}>
+ {length}
+ </div>
+ </div>
+ </div >;
+ }
+
render() {
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);
@@ -480,6 +534,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
TreeView.loadId = doc[Id];
Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true);
}} />
+ {this.props.Document.workspaceLibrary ? this.notifsButton : (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,
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index c1a6ca44e..1984965ba 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -122,7 +122,6 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
}
render() {
- trace();
return (
<CollectionBaseView {...this.props} className="collectionVideoView-cont" onContextMenu={this.onContextMenu}>
{this.subView}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 169dbe540..7a22b7ec3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,6 +1,6 @@
import { action, computed } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCastAsync, HeightSym, WidthSym } from "../../../../new_fields/Doc";
+import { Doc, DocListCastAsync, HeightSym, WidthSym, DocListCast } from "../../../../new_fields/Doc";
import { Id } from "../../../../new_fields/FieldSymbols";
import { InkField, StrokeData } from "../../../../new_fields/InkField";
import { createSchema, makeInterface } from "../../../../new_fields/Schema";
@@ -27,12 +27,18 @@ import "./CollectionFreeFormView.scss";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
import v5 = require("uuid/v5");
+import { ScriptField } from "../../../../new_fields/ScriptField";
+import { OverlayView, OverlayElementOptions } from "../../OverlayView";
+import { ScriptBox } from "../../ScriptBox";
+import { CompileScript } from "../../../util/Scripting";
export const panZoomSchema = createSchema({
panX: "number",
panY: "number",
- scale: "number"
+ scale: "number",
+ arrangeScript: ScriptField,
+ arrangeInit: ScriptField,
});
type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof positionSchema, typeof pageSchema]>;
@@ -50,9 +56,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@computed get nativeHeight() { return this.Document.nativeHeight || 0; }
public get isAnnotationOverlay() { return this.props.fieldKey === "annotations" || this.props.fieldExt === "annotations"; }
private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
- private panX = () => this.props.fitToBox ? this.props.fitToBox[0] : this.Document.panX || 0;
- private panY = () => this.props.fitToBox ? this.props.fitToBox[1] : this.Document.panY || 0;
- private zoomScaling = () => this.props.fitToBox ? this.props.fitToBox[2] : this.Document.scale || 1;
+ private panX = () => this.props.fitToBox ? this.props.fitToBox()[0] : this.Document.panX || 0;
+ private panY = () => this.props.fitToBox ? this.props.fitToBox()[1] : this.Document.panY || 0;
+ private zoomScaling = () => this.props.fitToBox ? this.props.fitToBox()[2] : this.Document.scale || 1;
private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 : 0; // shift so pan position is at center of window for non-overlay collections
private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 : 0;// shift so pan position is at center of window for non-overlay collections
private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform());
@@ -243,16 +249,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
var scale = this.getLocalTransform().inverse().Scale;
const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY));
- // this.props.Document.panX = this.isAnnotationOverlay ? newPanX : panX;
- // this.props.Document.panY = this.isAnnotationOverlay ? newPanY : panY;
- this.props.Document.panX = panX;
+ this.props.Document.panX = this.isAnnotationOverlay ? newPanX : panX;
+ this.props.Document.panY = this.isAnnotationOverlay ? newPanY : panY;
+ // this.props.Document.panX = panX;
+ // this.props.Document.panY = panY;
if (this.props.Document.scrollY) {
this.props.Document.scrollY = panY;
- this.props.Document.panY = panY;
- }
- else {
-
- this.props.Document.panY = panY;
}
}
@@ -388,16 +390,36 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
};
}
+ getCalculatedPositions(script: ScriptField, params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): { x?: number, y?: number, width?: number, height?: number, state?: any } {
+ const result = script.script.run(params);
+ if (!result.success) {
+ return {};
+ }
+ return result.result === undefined ? {} : result.result;
+ }
+
@computed.struct
get views() {
let curPage = FieldValue(this.Document.curPage, -1);
- let docviews = this.childDocs.reduce((prev, doc) => {
+ const initScript = this.Document.arrangeInit;
+ const script = this.Document.arrangeScript;
+ let state: any = undefined;
+ const docs = this.childDocs;
+ if (initScript) {
+ const initResult = initScript.script.run({ docs, collection: this.Document });
+ if (initResult.success) {
+ state = initResult.result;
+ }
+ }
+ let docviews = docs.reduce((prev, doc) => {
if (!(doc instanceof Doc)) return prev;
var page = NumCast(doc.page, -1);
if (Math.round(page) === Math.round(curPage) || page === -1) {
let minim = BoolCast(doc.isMinimized, false);
if (minim === undefined || !minim) {
- prev.push(<CollectionFreeFormDocumentView key={doc[Id]} {...this.getChildDocumentViewProps(doc)} />);
+ const pos = script ? this.getCalculatedPositions(script, { doc, index: prev.length, collection: this.Document, docs, state }) : {};
+ state = pos.state === undefined ? state : pos.state;
+ prev.push(<CollectionFreeFormDocumentView key={doc[Id]} x={pos.x} y={pos.y} width={pos.width} height={pos.height} {...this.getChildDocumentViewProps(doc)} />);
}
}
return prev;
@@ -438,6 +460,36 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
}
});
+ ContextMenu.Instance.addItem({
+ description: "Add freeform arrangement",
+ event: () => {
+ let addOverlay = (key: "arrangeScript" | "arrangeInit", options: OverlayElementOptions, params?: Record<string, string>, requiredType?: string) => {
+ let overlayDisposer: () => void;
+ const script = this.Document[key];
+ let originalText: string | undefined = undefined;
+ if (script) originalText = script.script.originalScript;
+ let scriptingBox = <ScriptBox initialText={originalText} onCancel={() => overlayDisposer()} onSave={(text, onError) => {
+ const script = CompileScript(text, {
+ params,
+ requiredType,
+ typecheck: false
+ });
+ if (!script.compiled) {
+ onError(script.errors.map(error => error.messageText).join("\n"));
+ return;
+ }
+ const docs = DocListCast(this.Document[this.props.fieldKey]);
+ docs.map(d => d.transition = "transform 1s");
+ this.Document[key] = new ScriptField(script);
+ overlayDisposer();
+ setTimeout(() => docs.map(d => d.transition = undefined), 1200);
+ }} />;
+ overlayDisposer = OverlayView.Instance.addElement(scriptingBox, options);
+ };
+ addOverlay("arrangeInit", { x: 400, y: 100, width: 400, height: 300 }, { collection: "Doc", docs: "Doc[]" }, undefined);
+ addOverlay("arrangeScript", { x: 400, y: 500, width: 400, height: 300 }, { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}");
+ }
+ });
}
private childViews = () => [
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index 6e8ec8662..9fc2e44fb 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -21,6 +21,6 @@
white-space:nowrap;
}
.marquee-legend::after {
- content: "Press: c (collection), s (summary), r (replace) or Delete"
+ content: "Press: c (collection), s (summary), or Delete"
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 2a78cda2f..4850c6218 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -44,14 +44,12 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
_commandExecuted = false;
@action
- cleanupInteractions = (all: boolean = false, rem_keydown: boolean = true) => {
+ cleanupInteractions = (all: boolean = false) => {
if (all) {
document.removeEventListener("pointerup", this.onPointerUp, true);
document.removeEventListener("pointermove", this.onPointerMove, true);
}
- if (rem_keydown) {
- document.removeEventListener("keydown", this.marqueeCommand, true);
- }
+ document.removeEventListener("keydown", this.marqueeCommand, true);
this._visible = false;
}
@@ -191,12 +189,9 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document);
}
this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]);
- mselect.length ? this.cleanupInteractions(true, false) : this.cleanupInteractions(true);
- }
- else {
- //console.log("invisible");
- this.cleanupInteractions(true);
}
+ //console.log("invisible");
+ this.cleanupInteractions(true);
if (e.altKey) {
e.preventDefault();
@@ -248,7 +243,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
this.cleanupInteractions(false);
e.stopPropagation();
}
- if (e.key === "c" || e.key === "s" || e.key === "S" || e.key === "e" || e.key === "p") {
+ if (e.key === "c" || e.key === "s" || e.key === "S" || e.key === "e") {
this._commandExecuted = true;
e.stopPropagation();
e.preventDefault();
@@ -266,18 +261,17 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
}
let ink = Cast(this.props.container.props.Document.ink, InkField);
let inkData = ink ? ink.inkData : undefined;
- let zoomBasis = NumCast(this.props.container.props.Document.scale, 1);
let newCollection = Docs.FreeformDocument(selected, {
x: bounds.left,
y: bounds.top,
panX: 0,
panY: 0,
- borderRounding: e.key === "e" ? -1 : undefined,
+ borderRounding: e.key === "e" ? "100%" : undefined,
backgroundColor: this.props.container.isAnnotationOverlay ? undefined : "white",
width: bounds.width,
height: bounds.height,
ink: inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined,
- title: e.key === "s" || e.key === "S" ? "-summary-" : e.key === "p" ? "-summary-" : "a nested collection",
+ title: e.key === "s" || e.key === "S" ? "-summary-" : "a nested collection",
});
this.marqueeInkDelete(inkData);
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 858959d81..1c00687ed 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,7 +1,7 @@
import { computed } from "mobx";
import { observer } from "mobx-react";
import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { BoolCast, FieldValue, NumCast } from "../../../new_fields/Types";
+import { BoolCast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
import { Transform } from "../../util/Transform";
import { DocComponent } from "../DocComponent";
import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView";
@@ -9,6 +9,10 @@ import "./DocumentView.scss";
import React = require("react");
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
+ x?: number;
+ y?: number;
+ width?: number;
+ height?: number;
}
const schema = createSchema({
@@ -23,13 +27,13 @@ const FreeformDocument = makeInterface(schema, positionSchema);
@observer
export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, FreeformDocument>(FreeformDocument) {
@computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}) `; }
- @computed get X() { return FieldValue(this.Document.x, 0); }
- @computed get Y() { return FieldValue(this.Document.y, 0); }
+ @computed get X() { return this.props.x !== undefined ? this.props.x : this.Document.x || 0; }
+ @computed get Y() { return this.props.y !== undefined ? this.props.y : this.Document.y || 0; }
+ @computed get width(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.props.width !== undefined ? this.props.width : this.Document.width || 0; }
+ @computed get height(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.props.height !== undefined ? this.props.height : this.Document.height || 0; }
@computed get zoom(): number { return 1 / FieldValue(this.Document.zoomBasis, 1); }
@computed get nativeWidth(): number { return FieldValue(this.Document.nativeWidth, 0); }
@computed get nativeHeight(): number { return FieldValue(this.Document.nativeHeight, 0); }
- @computed get width(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : FieldValue(this.Document.width, 0); }
- @computed get height(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : FieldValue(this.Document.height, 0); }
set width(w: number) {
this.Document.width = w;
@@ -43,12 +47,13 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
this.Document.width = this.nativeWidth / this.nativeHeight * h;
}
}
+ @computed get scaleToOverridingWidth() { return this.width / NumCast(this.props.Document.width, this.width); }
contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1;
panelWidth = () => this.props.PanelWidth();
panelHeight = () => this.props.PanelHeight();
getTransform = (): Transform => this.props.ScreenToLocalTransform()
.translate(-this.X, -this.Y)
- .scale(1 / this.contentScaling()).scale(1 / this.zoom)
+ .scale(1 / this.contentScaling()).scale(1 / this.zoom / this.scaleToOverridingWidth)
animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => {
this.props.bringToFront(this.props.Document);
@@ -65,11 +70,14 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}
borderRounding = () => {
- let br = NumCast(this.props.Document.borderRounding);
- return br >= 0 ? br :
- NumCast(this.props.Document.nativeWidth) === 0 ?
- Math.min(this.props.PanelWidth(), this.props.PanelHeight())
- : Math.min(this.Document.nativeWidth || 0, this.Document.nativeHeight || 0);
+ let br = StrCast(this.props.Document.borderRounding);
+ if (br.endsWith("%")) {
+ let percent = Number(br.substr(0, br.length - 1)) / 100;
+ let nativeDim = Math.min(NumCast(this.props.Document.nativeWidth), NumCast(this.props.Document.nativeHeight));
+ let minDim = percent * (nativeDim ? nativeDim : Math.min(this.props.PanelWidth(), this.props.PanelHeight()));
+ return minDim;
+ }
+ return undefined;
}
render() {
@@ -79,8 +87,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
transformOrigin: "left top",
position: "absolute",
backgroundColor: "transparent",
- borderRadius: `${this.borderRounding()}px`,
+ borderRadius: this.borderRounding(),
transform: this.transform,
+ transition: StrCast(this.props.Document.transition),
width: this.width,
height: this.height,
zIndex: this.Document.zIndex || 0,
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 0bfb2954f..cf4b9db44 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -25,6 +25,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";
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -83,7 +84,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
// by checking the layoutKey. This should probably be moved into
// a prop so that the overlay can explicitly turn off templates.
if ((this.props.layoutKey === "overlayLayout" && StrCast(this.props.Document.layout).indexOf("CollectionView") !== -1) ||
- (this.props.layoutKey === "layout" && StrCast(this.props.Document.layout).indexOf("CollectionView") === -1)) {
+ (this.props.layoutKey === "layout" && StrCast(this.props.Document.layout).indexOf("CollectionView") === -1) ||
+ (this.props.layoutKey === "layout" && NumCast(this.props.Document.viewType)) !== CollectionViewType.Freeform) {
this.templates.forEach(template => {
let self = this;
// this scales constants in the markup by the scaling applied to the document, but caps the constants to be smaller
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3673cb0e2..ef397a824 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -71,7 +71,7 @@ export interface DocumentViewProps {
ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
Document: Doc;
DataDoc?: Doc;
- fitToBox?: number[];
+ fitToBox?: () => number[];
addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean;
removeDocument?: (doc: Doc) => boolean;
moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
@@ -375,7 +375,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
onPointerMove = (e: PointerEvent): void => {
if (!e.cancelBubble && this.active) {
- if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
+ if (!this.props.Document.excludeFromLibrary && (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3)) {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.props.Document.lockedPosition)) {
@@ -573,8 +573,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@computed get nativeWidth() { return this.Document.nativeWidth || 0; }
@computed get nativeHeight() { return this.Document.nativeHeight || 0; }
@computed get contents() {
- return (
- <DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} selectOnLoad={this.props.selectOnLoad} layoutKey={"layout"} />);
+ return (<DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} selectOnLoad={this.props.selectOnLoad} layoutKey={"layout"} />);
}
render() {
@@ -590,8 +589,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
style={{
outlineColor: "maroon",
outlineStyle: "dashed",
- outlineWidth: BoolCast(this.props.Document.libraryBrush) || BoolCast(this.props.Document.protoBrush) ?
+ outlineWidth: BoolCast(this.props.Document.libraryBrush) && !StrCast(this.props.Document.borderRounding) ?
`${this.props.ScreenToLocalTransform().Scale}px` : "0px",
+ border: BoolCast(this.props.Document.libraryBrush) && StrCast(this.props.Document.borderRounding) ?
+ `dashed maroon ${this.props.ScreenToLocalTransform().Scale}px` : undefined,
borderRadius: "inherit",
background: backgroundColor,
width: nativeWidth,
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 3f5a2e744..c5fc6c65a 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -31,7 +31,7 @@ export interface FieldViewProps {
fieldKey: string;
fieldExt: string;
leaveNativeSize?: boolean;
- fitToBox?: number[];
+ fitToBox?: () => number[];
ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
Document: Doc;
DataDoc?: Doc;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 07cd43ce3..bf6f4c764 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -97,6 +97,15 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
return "";
}
+ @undoBatch
+ public setFontColor(color: string) {
+ if (this._editorView!.state.selection.from === this._editorView!.state.selection.to) return false;
+ let colorMark = this._editorView!.state.schema.mark(this._editorView!.state.schema.marks.pFontColor, { color: color });
+ this._editorView!.dispatch(this._editorView!.state.tr.addMark(this._editorView!.state.selection.from,
+ this._editorView!.state.selection.to, colorMark));
+ return true;
+ }
+
constructor(props: FieldViewProps) {
super(props);
if (this.props.outer_div) {
@@ -211,7 +220,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
const field = this.dataDoc ? Cast(this.dataDoc[this.props.fieldKey], RichTextField) : undefined;
return field ? field.Data : `{"doc":{"type":"doc","content":[]},"selection":{"type":"text","anchor":0,"head":0}}`;
},
- field => this._editorView && !this._applyingChange && this.props.Document[this.props.fieldKey] instanceof RichTextField &&
+ field => this._editorView && !this._applyingChange &&
this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field)))
);
this.setupEditor(config, this.dataDoc, this.props.fieldKey);
@@ -418,7 +427,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
render() {
let style = this.props.isOverlay ? "scroll" : "hidden";
- let rounded = NumCast(this.props.Document.borderRounding) < 0 ? "-rounded" : "";
+ let rounded = StrCast(this.props.Document.borderRounding) === "100%" ? "-rounded" : "";
let interactive = InkingControl.Instance.selectedTool ? "" : "interactive";
return (
<div className={`formattedTextBox-cont-${style}`} ref={this._ref}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 88c122b61..cc851826b 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -73,7 +73,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
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 {
+ } else if (de.mods === "CtrlKey") {
if (this.extensionDoc !== this.dataDoc) {
let layout = StrCast(drop.backgroundLayout);
if (layout.indexOf(ImageBox.name) !== -1) {
@@ -182,7 +182,8 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
return url.href;
}
let ext = path.extname(url.href);
- return url.href.replace(ext, this._curSuffix + ext);
+ const suffix = this.props.renderDepth <= 1 ? "_o" : this._curSuffix;
+ return url.href.replace(ext, suffix + ext);
}
@observable _smallRetryCount = 1;
@@ -205,6 +206,8 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
requestImageSize(window.origin + RouteStore.corsProxy + "/" + srcpath)
.then((size: any) => {
let aspect = size.height / size.width;
+ let rotation = NumCast(this.dataDoc.rotation) % 180;
+ if (rotation === 90 || rotation === 270) aspect = 1 / aspect;
if (Math.abs(layoutdoc[HeightSym]() / layoutdoc[WidthSym]() - aspect) > 0.01) {
setTimeout(action(() => {
layoutdoc.height = layoutdoc[WidthSym]() * aspect;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 0e798d291..9407d742c 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -9,7 +9,7 @@ import { KeyValuePair } from "./KeyValuePair";
import React = require("react");
import { NumCast, Cast, FieldValue, StrCast } from "../../../new_fields/Types";
import { Doc, Field, FieldResult } from "../../../new_fields/Doc";
-import { ComputedField } from "../../../new_fields/ScriptField";
+import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
import { SetupDrag } from "../../util/DragManager";
import { Docs } from "../../documents/Documents";
import { RawDataOperationParameters } from "../../northstar/model/idea/idea";
@@ -50,7 +50,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
let eq = value.startsWith("=");
let target = eq ? doc : Doc.GetProto(doc);
value = eq ? value.substr(1) : value;
- let dubEq = value.startsWith(":=");
+ let dubEq = value.startsWith(":=") ? 1 : value.startsWith(";=") ? 2 : 0;
value = dubEq ? value.substr(2) : value;
let options: ScriptOptions = { addReturn: true, params: { this: "Doc" } };
if (dubEq) options.typecheck = false;
@@ -58,8 +58,12 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
if (!script.compiled) {
return false;
}
- let field = new ComputedField(script);
- if (!dubEq) {
+ let field: Field;
+ if (dubEq === 1) {
+ field = new ComputedField(script);
+ } else if (dubEq === 2) {
+ field = new ScriptField(script);
+ } else {
let res = script.run({ this: target });
if (!res.success) return false;
field = res.result;
diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx
index 22da732cf..e6cc50620 100644
--- a/src/client/views/nodes/LinkEditor.tsx
+++ b/src/client/views/nodes/LinkEditor.tsx
@@ -271,7 +271,6 @@ export class LinkGroupEditor extends React.Component<LinkGroupEditorProps> {
</>
);
}
- trace();
return (
<div className="linkEditor-group">
<div className="linkEditor-group-row">
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 83dedb71d..cc02bb282 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -149,7 +149,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
scrollTo(y: number) {
if (this._mainCont.current) {
- this._mainCont.current.scrollTo({ top: y });
+ this._mainCont.current.scrollTo({ top: y, behavior: "auto" });
}
}
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 9718c1406..0a1661a1a 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -75,7 +75,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
() => this.props.parent.Index,
() => {
if (this.props.parent.Index === this.props.index) {
- this.props.parent.scrollTo(this.props.y - 50);
+ this.props.parent.scrollTo(this.props.y * scale - (NumCast(this.props.parent.props.parent.Document.pdfHeight) / 2));
}
}
);
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index f93b2e59f..d6970e7f4 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -3,11 +3,8 @@ import "./PDFMenu.scss";
import { observable, action, runInAction } from "mobx";
import { observer } from "mobx-react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { emptyFunction, returnZero, returnTrue, returnFalse } from "../../../Utils";
+import { emptyFunction, returnFalse } from "../../../Utils";
import { Doc } from "../../../new_fields/Doc";
-import { DragManager } from "../../util/DragManager";
-import { DocUtils } from "../../documents/Documents";
-import { PresentationView } from "../presentationview/PresentationView";
@observer
export default class PDFMenu extends React.Component {
@@ -20,7 +17,7 @@ export default class PDFMenu extends React.Component {
@observable private _transitionDelay: string = "";
- StartDrag: (e: PointerEvent) => void = emptyFunction;
+ StartDrag: (e: PointerEvent, ele: HTMLDivElement) => void = emptyFunction;
Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction;
Delete: () => void = emptyFunction;
Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = emptyFunction;
@@ -35,9 +32,10 @@ export default class PDFMenu extends React.Component {
private _offsetY: number = 0;
private _offsetX: number = 0;
- private _mainCont: React.RefObject<HTMLDivElement>;
+ private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
+ private _commentCont: React.RefObject<HTMLDivElement> = React.createRef();
+ private _snippetButton: React.RefObject<HTMLButtonElement> = React.createRef();
private _dragging: boolean = false;
- private _snippetButton: React.RefObject<HTMLButtonElement>;
@observable private _keyValue: string = "";
@observable private _valueValue: string = "";
@observable private _added: boolean = false;
@@ -46,9 +44,6 @@ export default class PDFMenu extends React.Component {
super(props);
PDFMenu.Instance = this;
-
- this._mainCont = React.createRef();
- this._snippetButton = React.createRef();
}
pointerDown = (e: React.PointerEvent) => {
@@ -69,7 +64,7 @@ export default class PDFMenu extends React.Component {
return;
}
- this.StartDrag(e);
+ this.StartDrag(e, this._commentCont.current!);
this._dragging = true;
}
@@ -246,7 +241,7 @@ export default class PDFMenu extends React.Component {
style={this.Highlighting ? { backgroundColor: "#121212" } : {}}>
<FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} />
</button>,
- <button className="pdfMenu-button" title="Drag to Annotate" onPointerDown={this.pointerDown}><FontAwesomeIcon icon="comment-alt" size="lg" key="2" /></button>,
+ <button className="pdfMenu-button" title="Drag to Annotate" ref={this._commentCont} onPointerDown={this.pointerDown}><FontAwesomeIcon icon="comment-alt" size="lg" key="2" /></button>,
this.Status === "snippet" ? <button className="pdfMenu-button" title="Drag to Snippetize Selection" onPointerDown={this.snippetStart} ref={this._snippetButton}><FontAwesomeIcon icon="cut" size="lg" /></button> : undefined,
<button className="pdfMenu-button" title="Pin Menu" onClick={this.togglePin} key="3"
style={this.Pinned ? { backgroundColor: "#121212" } : {}}>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 35bf1c4d7..8af29110f 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -166,6 +166,7 @@ export class Viewer extends React.Component<IViewerProps> {
}
});
}
+ this.Index = -1;
});
}
);
@@ -234,11 +235,15 @@ export class Viewer extends React.Component<IViewerProps> {
mainAnnoDoc.title = "Annotation on " + StrCast(this.props.parent.Document.title);
mainAnnoDoc.pdfDoc = this.props.parent.Document;
+ let minY = Number.MAX_VALUE;
this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => {
for (let anno of value) {
let annoDoc = new Doc();
if (anno.style.left) annoDoc.x = parseInt(anno.style.left) / scale;
- if (anno.style.top) annoDoc.y = parseInt(anno.style.top) / scale;
+ if (anno.style.top) {
+ annoDoc.y = parseInt(anno.style.top) / scale;
+ minY = Math.min(parseInt(anno.style.top), minY);
+ }
if (anno.style.height) annoDoc.height = parseInt(anno.style.height) / scale;
if (anno.style.width) annoDoc.width = parseInt(anno.style.width) / scale;
annoDoc.page = key;
@@ -251,12 +256,13 @@ export class Viewer extends React.Component<IViewerProps> {
}
});
- mainAnnoDoc.y = Math.max((NumCast(annoDocs[0].y) * scale) - 100, 0);
+ mainAnnoDoc.y = Math.max(minY, 0);
mainAnnoDoc.annotations = new List<Doc>(annoDocs);
if (sourceDoc) {
DocUtils.MakeLink(sourceDoc, mainAnnoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
}
this._savedAnnotations.clear();
+ this.Index = -1;
return mainAnnoDoc;
}
@@ -426,7 +432,7 @@ export class Viewer extends React.Component<IViewerProps> {
}
renderAnnotation = (anno: Doc, index: number): JSX.Element => {
- return <Annotation anno={anno} index={index} parent={this} />;
+ return <Annotation anno={anno} index={index} parent={this} key={`${anno[Id]}-annotation`} />;
}
@action
@@ -562,9 +568,10 @@ export class Viewer extends React.Component<IViewerProps> {
prevAnnotation = (e: React.MouseEvent) => {
e.stopPropagation();
- if (this.Index > 0) {
- this.Index--;
- }
+ // if (this.Index > 0) {
+ // this.Index--;
+ // }
+ this.Index = Math.max(this.Index - 1, 0);
}
@action
@@ -572,7 +579,7 @@ export class Viewer extends React.Component<IViewerProps> {
e.stopPropagation();
let compiled = this._script;
- if (this.Index < this._annotations.filter(anno => {
+ let filtered = this._annotations.filter(anno => {
if (compiled && compiled.compiled) {
let run = compiled.run({ this: anno });
if (run.success) {
@@ -580,9 +587,8 @@ export class Viewer extends React.Component<IViewerProps> {
}
}
return true;
- }).length - 1) {
- this.Index++;
- }
+ });
+ this.Index = Math.min(this.Index + 1, filtered.length - 1)
}
nextResult = () => {
@@ -630,7 +636,8 @@ export class Viewer extends React.Component<IViewerProps> {
}
}
return true;
- }).map((anno: Doc, index: number) => this.renderAnnotation(anno, index))}
+ }).sort((a: Doc, b: Doc) => NumCast(a.y) - NumCast(b.y))
+ .map((anno: Doc, index: number) => this.renderAnnotation(anno, index))}
</div>
</div>
<div className="pdfViewer-overlayCont" onPointerDown={(e) => e.stopPropagation()}
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 92f5390ae..49eac71c4 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -152,7 +152,7 @@ export default class Page extends React.Component<IPageProps> {
* start a drag event and create or put the necessary info into the drag event.
*/
@action
- startDrag = (e: PointerEvent): void => {
+ startDrag = (e: PointerEvent, ele: HTMLDivElement): void => {
e.preventDefault();
e.stopPropagation();
let thisDoc = this.props.parent.Document;
@@ -163,7 +163,7 @@ export default class Page extends React.Component<IPageProps> {
// create dragData and star tdrag
let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc);
if (this._textLayer.current) {
- DragManager.StartAnnotationDrag([this._textLayer.current], dragData, e.pageX, e.pageY, {
+ DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, {
handlers: {
dragComplete: emptyFunction,
},
diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx
index d63c0b066..6896ee452 100644
--- a/src/client/views/presentationview/PresentationElement.tsx
+++ b/src/client/views/presentationview/PresentationElement.tsx
@@ -377,7 +377,7 @@ export default class PresentationElement extends React.Component<PresentationEle
style={{
outlineColor: "maroon",
outlineStyle: "dashed",
- outlineWidth: BoolCast(p.document.libraryBrush, false) || BoolCast(p.document.protoBrush, false) ? `1px` : "0px",
+ outlineWidth: BoolCast(p.document.libraryBrush, false) ? `1px` : "0px",
}}
onClick={e => { p.gotoDocument(p.index, NumCast(this.props.mainDocument.selectedDoc)); e.stopPropagation(); }}>
<strong className="presentationView-name">
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 5a7bdd24d..6cedc7cfb 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -7,10 +7,10 @@ import { observer } from "mobx-react";
import { Doc, DocListCast, HeightSym, WidthSym } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { emptyFunction, returnFalse, returnOne } from "../../../Utils";
+import { emptyFunction, returnFalse, returnOne, Utils } from "../../../Utils";
import { DocTypes } from "../../documents/Documents";
import { DocumentManager } from "../../util/DocumentManager";
-import { SetupDrag } from "../../util/DragManager";
+import { SetupDrag, DragManager } from "../../util/DragManager";
import { LinkManager } from "../../util/LinkManager";
import { SearchUtil } from "../../util/SearchUtil";
import { Transform } from "../../util/Transform";
@@ -21,6 +21,8 @@ import { DocumentView } from "../nodes/DocumentView";
import { SearchBox } from "./SearchBox";
import "./SearchItem.scss";
import "./SelectorContextMenu.scss";
+import { ContextMenu } from "../ContextMenu";
+import { faFile } from '@fortawesome/free-solid-svg-icons';
export interface SearchItemProps {
doc: Doc;
@@ -29,6 +31,7 @@ export interface SearchItemProps {
library.add(faCaretUp);
library.add(faObjectGroup);
library.add(faStickyNote);
+library.add(faFile);
library.add(faFilePdf);
library.add(faFilm);
library.add(faMusic);
@@ -101,34 +104,23 @@ export class SearchItem extends React.Component<SearchItemProps> {
@observable _useIcons = true;
@observable _displayDim = 50;
+ fitToBox = () => {
+ let bounds = Doc.ComputeContentBounds(this.props.doc);
+ return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Number(SEARCH_THUMBNAIL_SIZE) / Math.max((bounds.b - bounds.y), (bounds.r - bounds.x)), this._displayDim];
+ }
@computed
public get DocumentIcon() {
- let layoutresult = StrCast(this.props.doc.type);
if (!this._useIcons) {
- let renderDoc = this.props.doc;
- let box: number[] = [];
- if (layoutresult.indexOf(DocTypes.COL) !== -1) {
- renderDoc = Doc.MakeDelegate(renderDoc);
- let bounds = DocListCast(renderDoc.data).reduce((bounds, doc) => {
- var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
- let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
- return {
- x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
- r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b)
- };
- }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE });
- box = [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Number(SEARCH_THUMBNAIL_SIZE) / (bounds.r - bounds.x), this._displayDim];
- }
let returnXDimension = () => this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE);
let returnYDimension = () => this._displayDim;
- let scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension());
+ let scale = () => returnXDimension() / NumCast(this.props.doc.nativeWidth, returnXDimension());
return <div
onPointerDown={action(() => { this._useIcons = !this._useIcons; this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE); })}
onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE))}
onPointerLeave={action(() => this._displayDim = 50)} >
<DocumentView
- fitToBox={box}
- Document={renderDoc}
+ fitToBox={StrCast(this.props.doc.type).indexOf(DocTypes.COL) !== -1 ? this.fitToBox : undefined}
+ Document={this.props.doc}
addDocument={returnFalse}
removeDocument={returnFalse}
ScreenToLocalTransform={Transform.Identity}
@@ -149,6 +141,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
</div>;
}
+ let layoutresult = StrCast(this.props.doc.type);
let button = layoutresult.indexOf(DocTypes.PDF) !== -1 ? faFilePdf :
layoutresult.indexOf(DocTypes.IMG) !== -1 ? faImage :
layoutresult.indexOf(DocTypes.TEXT) !== -1 ? faStickyNote :
@@ -188,7 +181,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
}
@action
- pointerDown = (e: React.PointerEvent) => SearchBox.Instance.openSearch(e)
+ pointerDown = (e: React.PointerEvent) => { e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e); }
highlightDoc = (e: React.PointerEvent) => {
if (this.props.doc.type === DocTypes.LINK) {
@@ -224,12 +217,35 @@ export class SearchItem extends React.Component<SearchItemProps> {
}
}
+ onContextMenu = (e: React.MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ ContextMenu.Instance.clearItems();
+ ContextMenu.Instance.addItem({
+ description: "Copy ID", event: () => {
+ Utils.CopyText(this.props.doc[Id]);
+ }
+ });
+ ContextMenu.Instance.displayMenu(e.clientX, e.clientY);
+ }
+
+ onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
+ e.stopPropagation();
+ e.preventDefault();
+ const doc = Doc.IsPrototype(this.props.doc) ? Doc.MakeDelegate(this.props.doc) : this.props.doc;
+ DragManager.StartDocumentDrag([e.currentTarget], new DragManager.DocumentDragData([doc], []), e.clientX, e.clientY, {
+ handlers: { dragComplete: emptyFunction },
+ hideSource: false,
+ });
+ }
+
render() {
return (
- <div className="search-overview" onPointerDown={this.pointerDown}>
- <div className="search-item" onPointerEnter={this.highlightDoc} onPointerLeave={this.unHighlightDoc} ref={this.collectionRef} id="result"
+ <div className="search-overview" onPointerDown={this.pointerDown} onContextMenu={this.onContextMenu}>
+ <div className="search-item" onPointerEnter={this.highlightDoc} onPointerLeave={this.unHighlightDoc} id="result"
onClick={this.onClick} onPointerDown={this.pointerDown} >
<div className="main-search-info">
+ <div title="Drag as document" onPointerDown={this.onPointerDown}> <FontAwesomeIcon icon="file" size="lg" /> </div>
<div className="search-title" id="result" >{this.props.doc.title}</div>
<div className="search-info" style={{ width: this._useIcons ? "15%" : "400px" }}>
<div className={`icon-${this._useIcons ? "icons" : "live"}`}>
diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx
index b22300d0b..f48eb696c 100644
--- a/src/debug/Viewer.tsx
+++ b/src/debug/Viewer.tsx
@@ -10,6 +10,15 @@ import { List } from '../new_fields/List';
import { URLField } from '../new_fields/URLField';
import { EditableView } from '../client/views/EditableView';
import { CompileScript } from '../client/util/Scripting';
+import { DateField } from '../new_fields/DateField';
+import { ScriptField } from '../new_fields/ScriptField';
+import CursorField from '../new_fields/CursorField';
+
+DateField;
+URLField;
+ScriptField;
+CursorField;
+
function applyToDoc(doc: { [index: string]: FieldResult }, key: string, scriptString: string): boolean;
function applyToDoc(doc: { [index: number]: FieldResult }, key: number, scriptString: string): boolean;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 734a90731..092205f52 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -8,6 +8,7 @@ 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 } from "../client/util/Scripting";
export namespace Field {
export function toScriptString(field: Field): string {
@@ -55,6 +56,7 @@ export function DocListCast(field: FieldResult): Doc[] {
export const WidthSym = Symbol("Width");
export const HeightSym = Symbol("Height");
+@scriptingGlobal
@Deserializable("doc").withFields(["id"])
export class Doc extends RefField {
constructor(id?: FieldId, forceSave?: boolean) {
@@ -239,16 +241,31 @@ export namespace Doc {
return Array.from(results);
}
- export function AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean) {
+ export function AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean) {
let list = Cast(target[key], listSpec(Doc));
if (list) {
- let ind = relativeTo ? list.indexOf(relativeTo) : -1;
- if (ind === -1) list.push(doc);
- else list.splice(before ? ind : ind + 1, 0, doc);
+ if (first) list.splice(0, 0, doc);
+ else {
+ let ind = relativeTo ? list.indexOf(relativeTo) : -1;
+ if (ind === -1) list.push(doc);
+ else list.splice(before ? ind : ind + 1, 0, doc);
+ }
}
return true;
}
+ export function ComputeContentBounds(doc: Doc) {
+ let bounds = DocListCast(doc.data).reduce((bounds, doc) => {
+ var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
+ let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
+ return {
+ x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
+ r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b)
+ };
+ }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE });
+ return bounds;
+ }
+
//
// Resolves a reference to a field by returning 'doc' if o field extension is specified,
// otherwise, it returns the extension document stored in doc.<fieldKey>_ext.
diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts
index f1e4c4721..a2133a990 100644
--- a/src/new_fields/List.ts
+++ b/src/new_fields/List.ts
@@ -7,6 +7,7 @@ import { ObjectField } from "./ObjectField";
import { RefField } from "./RefField";
import { ProxyField } from "./Proxy";
import { Self, Update, Parent, OnUpdate, SelfProxy, ToScriptString, Copy } from "./FieldSymbols";
+import { Scripting } from "../client/util/Scripting";
const listHandlers: any = {
/// Mutator methods
@@ -294,4 +295,6 @@ class ListImpl<T extends Field> extends ObjectField {
}
}
export type List<T extends Field> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[];
-export const List: { new <T extends Field>(fields?: T[]): List<T> } = ListImpl as any; \ No newline at end of file
+export const List: { new <T extends Field>(fields?: T[]): List<T> } = ListImpl as any;
+
+Scripting.addGlobal("List", List); \ No newline at end of file
diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts
index 89d077a47..78a3a4067 100644
--- a/src/new_fields/RichTextField.ts
+++ b/src/new_fields/RichTextField.ts
@@ -2,7 +2,9 @@ import { ObjectField } from "./ObjectField";
import { serializable } from "serializr";
import { Deserializable } from "../client/util/SerializationHelper";
import { Copy, ToScriptString } from "./FieldSymbols";
+import { scriptingGlobal } from "../client/util/Scripting";
+@scriptingGlobal
@Deserializable("RichTextField")
export class RichTextField extends ObjectField {
@serializable(true)
diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts
index 3d56e9374..e2994ed70 100644
--- a/src/new_fields/ScriptField.ts
+++ b/src/new_fields/ScriptField.ts
@@ -1,5 +1,5 @@
import { ObjectField } from "./ObjectField";
-import { CompiledScript, CompileScript } from "../client/util/Scripting";
+import { CompiledScript, CompileScript, scriptingGlobal } from "../client/util/Scripting";
import { Copy, ToScriptString, Parent, SelfProxy } from "./FieldSymbols";
import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr";
import { Deserializable } from "../client/util/SerializationHelper";
@@ -40,6 +40,7 @@ function deserializeScript(script: ScriptField) {
(script as any).script = comp;
}
+@scriptingGlobal
@Deserializable("script", deserializeScript)
export class ScriptField extends ObjectField {
@serializable(object(scriptSchema))
@@ -81,6 +82,7 @@ export class ScriptField extends ObjectField {
}
}
+@scriptingGlobal
@Deserializable("computed", deserializeScript)
export class ComputedField extends ScriptField {
//TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc
diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts
index 4a2841fb6..d935a61af 100644
--- a/src/new_fields/URLField.ts
+++ b/src/new_fields/URLField.ts
@@ -2,6 +2,7 @@ import { Deserializable } from "../client/util/SerializationHelper";
import { serializable, custom } from "serializr";
import { ObjectField } from "./ObjectField";
import { ToScriptString, Copy } from "./FieldSymbols";
+import { Scripting, scriptingGlobal } from "../client/util/Scripting";
function url() {
return custom(
@@ -37,8 +38,8 @@ export abstract class URLField extends ObjectField {
}
}
-@Deserializable("audio") export class AudioField extends URLField { }
-@Deserializable("image") export class ImageField extends URLField { }
-@Deserializable("video") export class VideoField extends URLField { }
-@Deserializable("pdf") export class PdfField extends URLField { }
-@Deserializable("web") export class WebField extends URLField { } \ No newline at end of file
+@scriptingGlobal @Deserializable("audio") export class AudioField extends URLField { }
+@scriptingGlobal @Deserializable("image") export class ImageField extends URLField { }
+@scriptingGlobal @Deserializable("video") export class VideoField extends URLField { }
+@scriptingGlobal @Deserializable("pdf") export class PdfField extends URLField { }
+@scriptingGlobal @Deserializable("web") export class WebField extends URLField { } \ No newline at end of file
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index abb777adf..b5e50d501 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -2,7 +2,6 @@ import { UndoManager } from "../client/util/UndoManager";
import { Doc, Field } from "./Doc";
import { SerializationHelper } from "../client/util/SerializationHelper";
import { ProxyField } from "./Proxy";
-import { FieldValue } from "./Types";
import { RefField } from "./RefField";
import { ObjectField } from "./ObjectField";
import { action } from "mobx";
@@ -13,6 +12,7 @@ function _readOnlySetter(): never {
throw new Error("Documents can't be modified in read-only mode");
}
const _setterImpl = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean {
+ //console.log("-set " + target[SelfProxy].title + "(" + target[SelfProxy][prop] + ")." + prop.toString() + " = " + value);
if (SerializationHelper.IsSerializing()) {
target[prop] = value;
return true;
diff --git a/src/server/GarbageColletor.ts b/src/server/GarbageColletor.ts
new file mode 100644
index 000000000..f26b0cec6
--- /dev/null
+++ b/src/server/GarbageColletor.ts
@@ -0,0 +1,51 @@
+import { Database } from './database';
+
+function addDoc(doc: any, ids: string[]) {
+ for (const key in doc) {
+ if (!doc.hasOwnProperty(key)) {
+ continue;
+ }
+ const field = doc[key];
+ if (!(field instanceof Object)) {
+ continue;
+ }
+ if (field.__type === "proxy") {
+ ids.push(field.fieldId);
+ } else if (field.__type === "list") {
+ addDoc(field.fields, ids);
+ }
+ }
+}
+
+async function GarbageCollect() {
+ // await new Promise(res => setTimeout(res, 3000));
+ const cursor = await Database.Instance.query({}, 'users');
+ const users = await cursor.toArray();
+ const ids: string[] = users.map(user => user.userDocumentId);
+ const visited = new Set<string>();
+
+ while (ids.length) {
+ const id = ids.pop()!;
+ if (visited.has(id)) continue;
+ const doc = await new Promise<{ [key: string]: any }>(res => Database.Instance.getDocument(id, res, "newDocuments"));
+ if (doc === undefined) {
+ console.log(`Couldn't find field with Id ${id}`);
+ continue;
+ }
+ visited.add(id);
+ addDoc(doc.fields, ids);
+ console.log(`To Go: ${ids.length}, visited: ${visited.size}`);
+ }
+
+ console.log(`Done: ${visited.size}`);
+
+ cursor.close();
+
+ const toDeleteCursor = await Database.Instance.query({ _id: { $nin: Array.from(visited) } });
+ const toDelete = (await toDeleteCursor.toArray()).map(doc => doc._id);
+ toDeleteCursor.close();
+ const result = await Database.Instance.delete({ _id: { $in: toDelete } }, "newDocuments");
+ console.log(`${result.deletedCount} documents deleted`);
+}
+
+GarbageCollect();
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 816c5f269..e328d6e5c 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -10,7 +10,7 @@ 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 } from "../../../new_fields/Types";
+import { Cast, FieldValue } from "../../../new_fields/Types";
import { RouteStore } from "../../RouteStore";
export class CurrentUserUtils {
@@ -32,7 +32,11 @@ export class CurrentUserUtils {
doc.dropAction = "alias";
doc.layout = CollectionView.LayoutString();
doc.title = this.email;
+ this.updateUserDocument(doc);
doc.data = new List<Doc>();
+ doc.gridGap = 5;
+ doc.xMargin = 5;
+ doc.yMargin = 5;
doc.excludeFromLibrary = true;
doc.optionalRightCollection = Docs.StackingDocument([], { title: "New mobile uploads" });
// doc.library = Docs.TreeDocument([doc], { title: `Library: ${CurrentUserUtils.email}` });
@@ -40,6 +44,29 @@ export class CurrentUserUtils {
return doc;
}
+ static updateUserDocument(doc: Doc) {
+ if (doc.workspaces === undefined) {
+ const workspaces = Docs.TreeDocument([], { title: "Workspaces", height: 100 });
+ workspaces.excludeFromLibrary = true;
+ workspaces.workspaceLibrary = true;
+ doc.workspaces = workspaces;
+ }
+ if (doc.recentlyClosed === undefined) {
+ const recentlyClosed = Docs.TreeDocument([], { title: "Recently Closed", height: 75 });
+ recentlyClosed.excludeFromLibrary = true;
+ doc.recentlyClosed = recentlyClosed;
+ }
+ if (doc.sidebar === undefined) {
+ const sidebar = Docs.StackingDocument([doc.workspaces as Doc, doc, doc.recentlyClosed as Doc], { title: "Sidebar" });
+ sidebar.excludeFromLibrary = true;
+ sidebar.gridGap = 5;
+ sidebar.xMargin = 5;
+ sidebar.yMargin = 5;
+ doc.sidebar = sidebar;
+ }
+
+ }
+
public static async loadCurrentUser(): Promise<any> {
let userPromise = rp.get(DocServer.prepend(RouteStore.getCurrUser)).then(response => {
if (response) {
@@ -52,8 +79,14 @@ export class CurrentUserUtils {
});
let userDocPromise = await rp.get(DocServer.prepend(RouteStore.getUserDocumentId)).then(id => {
if (id) {
- return DocServer.GetRefField(id).then(field =>
- runInAction(() => this.user_document = field instanceof Doc ? field : this.createUserDocument(id)));
+ return DocServer.GetRefField(id).then(async field => {
+ if (field instanceof Doc) {
+ await this.updateUserDocument(field);
+ runInAction(() => this.user_document = field);
+ } else {
+ runInAction(() => this.user_document = this.createUserDocument(id));
+ }
+ });
} else {
throw new Error("There should be a user id! Why does Dash think there isn't one?");
}
diff --git a/src/server/database.ts b/src/server/database.ts
index d240bd909..a17447629 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -41,11 +41,17 @@ export class Database {
}
}
- public delete(id: string, collectionName = Database.DocumentsCollection) {
+ public delete(query: any, collectionName?: string): Promise<mongodb.DeleteWriteOpResultObject>;
+ public delete(id: string, collectionName?: string): Promise<mongodb.DeleteWriteOpResultObject>;
+ public delete(id: any, collectionName = Database.DocumentsCollection) {
+ if (typeof id === "string") {
+ id = { _id: id };
+ }
if (this.db) {
- this.db.collection(collectionName).remove({ id: id });
+ const db = this.db;
+ return new Promise(res => db.collection(collectionName).deleteMany(id, (err, result) => res(result)));
} else {
- this.onConnect.push(() => this.delete(id, collectionName));
+ return new Promise(res => this.onConnect.push(() => res(this.delete(id, collectionName))));
}
}
@@ -125,7 +131,7 @@ export class Database {
return Promise.resolve<mongodb.Cursor>(this.db.collection(collectionName).find(query));
} else {
return new Promise<mongodb.Cursor>(res => {
- this.onConnect.push(() => res(this.query(query)));
+ this.onConnect.push(() => res(this.query(query, collectionName)));
});
}
}
diff --git a/src/server/index.ts b/src/server/index.ts
index a93940907..bf946fc9f 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -295,9 +295,10 @@ app.post(
const file = path.basename(files[name].path);
const ext = path.extname(file);
let resizers = [
- { resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: "_s" },
- { resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: "_m" },
- { resizer: sharp().resize(900, undefined, { withoutEnlargement: true }), suffix: "_l" },
+ { resizer: sharp().rotate(), suffix: "_o" },
+ { resizer: sharp().resize(100, undefined, { withoutEnlargement: true }).rotate(), suffix: "_s" },
+ { resizer: sharp().resize(400, undefined, { withoutEnlargement: true }).rotate(), suffix: "_m" },
+ { resizer: sharp().resize(900, undefined, { withoutEnlargement: true }).rotate(), suffix: "_l" },
];
let isImage = false;
if (pngTypes.includes(ext)) {
diff --git a/tance.jumpToDocument(linkedFwdDocs[altKey  1  0], ctrlKey, false, document = this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey  1  0], targetContext); b/tance.jumpToDocument(linkedFwdDocs[altKey  1  0], ctrlKey, false, document = this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey  1  0], targetContext);
deleted file mode 100644
index 0aa3ad47b..000000000
--- a/tance.jumpToDocument(linkedFwdDocs[altKey  1  0], ctrlKey, false, document = this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey  1  0], targetContext);
+++ /dev/null
@@ -1,792 +0,0 @@
-commit cc1f3b32d60786b56280a8b3c00059aa7823af89
-Merge: a81677c deb8576
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Wed Jun 26 14:54:46 2019 -0400
-
- merge
-
-commit a81677c7dffafa5134d4c5cbe893f7a886eaab63
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Wed Jun 26 14:48:16 2019 -0400
-
- can clear links on a doc
-
-commit 69e37491908b5c189b94f780994c1f142c69be2e
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Wed Jun 26 14:15:40 2019 -0400
-
- minor changes
-
-commit deb85766ac5648cc8e3ab4bf9d182ac5bbbbe144
-Merge: 219cabb 5e47775
-Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com>
-Date: Wed Jun 26 12:51:18 2019 -0400
-
- Merge pull request #170 from browngraphicslab/presentation-selection-mohammad
-
- Presentation selection mohammad
-
-commit 5e477755b392128ab8b39c082f16dd67708be0d2
-Merge: 444f970 6d1f161
-Author: Sam Wilkins <samuel_wilkins@brown.edu>
-Date: Wed Jun 26 12:48:45 2019 -0400
-
- Merge branch 'presentation-selection-mohammad' of https://github.com/browngraphicslab/Dash-Web into presentation-selection-mohammad
-
-commit 444f970365a4280376e929e78c16090f6ae92739
-Merge: 64ffa0a 219cabb
-Author: Sam Wilkins <samuel_wilkins@brown.edu>
-Date: Wed Jun 26 12:48:40 2019 -0400
-
- merged with master
-
-commit 6d1f161de3c27ec07673b5e48a915961177b57b6
-Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com>
-Date: Wed Jun 26 12:39:54 2019 -0400
-
- long line wrap
-
-commit f0632e4f6b608d05ef6d9f77d93da259c58c1e8d
-Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com>
-Date: Wed Jun 26 12:33:16 2019 -0400
-
- long line wrap
-
-commit 0d5e2537520ca1e6a6b52f4d0f03aa2bcfc6c5c6
-Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com>
-Date: Wed Jun 26 12:30:16 2019 -0400
-
- cleanup
-
-commit 8954bac59b50aa3618625379a17dbefe9aceca72
-Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com>
-Date: Wed Jun 26 12:29:07 2019 -0400
-
- removed console.logs
-
-commit d0ff42632f8a155303e11945a1a974a15052f0db
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Wed Jun 26 11:40:36 2019 -0400
-
- link menu styling
-
-commit a3c4aa24a9e9074da8f2421954f610c8178e10b1
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Tue Jun 25 21:28:15 2019 -0400
-
- link metadata values appear on first load
-
-commit ca8a78de9957ad27d345ad51fdaee9dae3f096bd
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Tue Jun 25 20:44:34 2019 -0400
-
- can't link to containing collection
-
-commit 2d300b0cd3d02c900865c61eacd539efed5289e6
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Tue Jun 25 20:18:14 2019 -0400
-
- fixed link metadata rendering bug
-
-commit 2a698e88da5ef0a9fee1ff4ee69746f1242798c9
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Tue Jun 25 18:32:17 2019 -0400
-
- fixed render links in treeview
-
-commit 7abe170ce5bd0c415e23456eb2bed26e8fdee7aa
-Merge: 41cf1e8 219cabb
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Tue Jun 25 18:23:26 2019 -0400
-
- merge
-
-commit 41cf1e8536964764f18ab752140e484e36cbe464
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Tue Jun 25 17:09:36 2019 -0400
-
- links can save
-
-commit 64ffa0accfc872c81035079527952aabaf56c6f6
-Author: Mohammad Amoush <mohammad_amoush@brown.edu>
-Date: Tue Jun 25 13:16:45 2019 -0400
-
- Small Css Fix On weight
-
-commit 219cabb3fe42ab199550efc3423b7aaed4e1ee93
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Mon Jun 24 22:45:19 2019 -0400
-
- Switched shift drag of tabs to normal drag and added drag target for document drag
-
-commit d475b19e9ba7bc8870ec7bc1e10b5cc88decea0b
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Mon Jun 24 15:56:42 2019 -0400
-
- fixed crash
-
-commit 522970375fe0227f9221a7e8be02875afd74ca63
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Mon Jun 24 14:01:29 2019 -0400
-
- link menu styling
-
-commit addf0e443f64951a437701f0d5a087c1d5968faf
-Merge: c9f77d5 d01039b
-Author: tschicke-brown <tyler_schicke@brown.edu>
-Date: Mon Jun 24 13:57:02 2019 -0400
-
- Merge pull request #167 from browngraphicslab/schema_fixes
-
- Schema and scripting fixes
-
-commit d01039b10f0ebd328224c0b1a190b0f884a7c727
-Merge: 6abf829 c9f77d5
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Mon Jun 24 13:56:30 2019 -0400
-
- Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web into schema_fixes
-
-commit c9f77d5aab98e6e7865cdcad957d5c937631775d
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Mon Jun 24 13:41:39 2019 -0400
-
- Added ReadOnly mode for docs and changed computed values a bit
-
-commit e18662f2fa9e1d3dd1b0eb3b5531092258d05972
-Author: Mohammad Amoush <mohammad_amoush@brown.edu>
-Date: Mon Jun 24 12:42:44 2019 -0400
-
- Refactoring
-
-commit 52051829373bc4acfe9d705b64c30e3fddebf439
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Mon Jun 24 10:49:05 2019 -0400
-
- Fixed image size stuff
-
-commit ac781d2fb714ca26fb364d00d5aeb7a20b008655
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Mon Jun 24 10:26:57 2019 -0400
-
- Changed how zooming works
-
-commit 6e5cd0e991e2e6d7ae8de1d73ff273ba0737355c
-Author: Tyler Schicke <tschicke@gmail.com>
-Date: Sun Jun 23 17:23:33 2019 -0400
-
- Fixed shift dragging with no open panes
-
-commit 32ef8d83d5829e2faadbebaf6f9b694df5d7ea02
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Fri Jun 21 17:41:20 2019 -0400
-
- link menu styling
-
-commit 7962aff8431b692af5229cd8e6c390bbe1110336
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Fri Jun 21 16:29:31 2019 -0400
-
- link menu styling
-
-commit a4b34adcb34184728be0b69b33a561f6d10f0a98
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Fri Jun 21 16:27:03 2019 -0400
-
- can drag just a group of links on a doc
-
-commit e1f5f341854944c533efdb7d36306edd1e1dc747
-Author: Mohammad Amoush <mohammad_amoush@brown.edu>
-Date: Fri Jun 21 14:53:08 2019 -0400
-
- Some More documentation
-
-commit 542f25d4af36cf0948696d45afba2e9e19f5bc37
-Author: Mohammad Amoush <mohammad_amoush@brown.edu>
-Date: Fri Jun 21 14:47:11 2019 -0400
-
- Redo Grouping Fixed
-
-commit 60f9122ea31d660d60d5429890c4eb0ef6d8613b
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Fri Jun 21 13:41:25 2019 -0400
-
- following link without viewdoc opens it to right
-
-commit d78c651322ad228152b862eaa378946fe65cc9f9
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Fri Jun 21 13:32:23 2019 -0400
-
- dragged links from menu are aliases
-
-commit 179afa6e80631fcb8899408c3961bf1757e5b19b
-Merge: ca5e29f a40e7bb
-Author: Bob Zeleznik <zzzman@gmail.com>
-Date: Thu Jun 20 22:23:40 2019 -0400
-
- Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
-
-commit ca5e29fdc7c238274eaf90682a8fa2ddc90e4e17
-Author: Bob Zeleznik <zzzman@gmail.com>
-Date: Thu Jun 20 22:22:57 2019 -0400
-
- fix to open on right, fix to image drag fro web, and layout fixes for stacking view multi-column
-
-commit a40e7bb5e9d1256002083d7e3f3c4db60cd8e9df
-Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com>
-Date: Thu Jun 20 19:41:39 2019 -0400
-
- Fixed missed pointer up event
-
-commit f4b75a7c921181faeeee04fbd57cd24fbd57523e
-Author: Mohammad Amoush <mohammad_amoush@brown.edu>
-Date: Thu Jun 20 19:16:42 2019 -0400
-
- Undo/Redo First Version
-
-commit b1a2871fcca57ce934b8613b315a08eede188669
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Thu Jun 20 19:03:16 2019 -0400
-
- link menu styling
-
-commit f2b54dc49205f8ea8944e26e43662a0c8dd08ed0
-Merge: 0cab79a 7d0f6c1
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Thu Jun 20 18:36:04 2019 -0400
-
- Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web
-
-commit 0cab79a50719719e1dade40520a6967f7aa8f951
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Thu Jun 20 18:35:45 2019 -0400
-
- Added debug and release modes to server and client
-
-commit fbfe9faca199b6dedd6844f1fa20cc02060a3c5a
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Thu Jun 20 18:25:49 2019 -0400
-
- can see what docs are linked to in treeview:
-
-commit 7d0f6c18489f7155818611721985d9610b08d8e7
-Merge: d2dfc0f 46a2a9e
-Author: yipstanley <stanley_yip@brown.edu>
-Date: Thu Jun 20 17:50:46 2019 -0400
-
- Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
-
-commit 1f172642d12c4669960b8526324e4bd034994be4
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Thu Jun 20 17:44:24 2019 -0400
-
- Added arrange documents in grid command
-
-commit d2dfc0f9d35f0084a7c0dea73215f5d21055f2f3
-Author: yipstanley <stanley_yip@brown.edu>
-Date: Thu Jun 20 17:17:14 2019 -0400
-
- pdf page sizes loading error
-
-commit e6ebed17e6ddb2ccee81d65fcb451a9b54302762
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Thu Jun 20 17:12:48 2019 -0400
-
- links can be made from freeform view to treeview
-
-commit 46a2a9e1f10b63feeb21a1e186daeaef2ccbcda4
-Merge: a39b285 a5dc0e0
-Author: bob <bcz@cs.brown.edu>
-Date: Thu Jun 20 17:11:29 2019 -0400
-
- Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
-
-commit a39b2854b848006c19460685d7bf4005a9f650ae
-Author: bob <bcz@cs.brown.edu>
-Date: Thu Jun 20 17:09:50 2019 -0400
-
- moved AddDocToList to Doc utils
-
-commit a5dc0e04add05f2f5bf1e17f1ac0a5e0aba1ea41
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Thu Jun 20 16:27:44 2019 -0400
-
- Added hidden flag to documents
-
-commit e88538bb8af2ba648da2326d0f6edd3e0186766e
-Author: Mohammad Amoush <mohammad_amoush@brown.edu>
-Date: Thu Jun 20 15:45:07 2019 -0400
-
- Title changing to presentations added
-
-commit 9b3e80def0be6c09c31b5176817a54323d217d81
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Thu Jun 20 15:06:41 2019 -0400
-
- Handled more events in editable view
-
-commit 1f24c5010a1cf6365265ea1f02327bb81a98134a
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Thu Jun 20 14:54:55 2019 -0400
-
- Doc.GetProto change and swapped KVP syntax
-
-commit 4360287e6cafcb59af1ae62fc31ddc161bcf2e51
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Thu Jun 20 12:56:13 2019 -0400
-
- styling of link proxy
-
-commit 711abbeba69e4d9afc634b8edf019b12b6dff915
-Author: Mohammad Amoush <mohammad_amoush@brown.edu>
-Date: Thu Jun 20 12:54:41 2019 -0400
-
- Documentation and reset Presentation at removal fixed
-
-commit a0246ef84396545f79fc4a8b21de1a56cbf06aca
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Thu Jun 20 11:34:28 2019 -0400
-
- merge
-
-commit 8dbfb3029a99eaf37a5234e9d9e33cc64f779b03
-Merge: af8e5cf e9d62f4
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Thu Jun 20 11:33:01 2019 -0400
-
- Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web
-
-commit af8e5cf1bfbfa2d57b4fd89c72306a71d8cabe1d
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Thu Jun 20 11:32:54 2019 -0400
-
- Fixed context menu search
-
-commit cd2db5bf11fb89e3cd7016f7f798d65698c74c5e
-Merge: 73f0378 e9d62f4
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Thu Jun 20 11:31:15 2019 -0400
-
- merge
-
-commit 73f03785f938542a91b28b35043f2feda2bc1432
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Thu Jun 20 11:26:33 2019 -0400
-
- merge
-
-commit e9d62f4ca0dbeb57e46239047041a8a04da7b504
-Author: bob <bcz@cs.brown.edu>
-Date: Thu Jun 20 11:26:16 2019 -0400
-
- changed color picker. fixed delting selected docs. fixed scaling items in nested panels.
-
-commit a5478b2d4cc3b66c6b58471cbb05c623d0109724
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Thu Jun 20 10:04:51 2019 -0400
-
- "Fixed" search
-
-commit 01aee875e626c695fe208addaaa6f58aad387dd6
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Thu Jun 20 10:02:08 2019 -0400
-
- Mostly keep context menu on screen
-
-commit 38de022621175bda7410df4444fcd2bbee0919cb
-Author: Bob Zeleznik <zzzman@gmail.com>
-Date: Wed Jun 19 23:43:47 2019 -0400
-
- slight tweaks.
-
-commit 9e55bfaad39aa47ab0594c6af7f1aa68e2a8db7a
-Merge: 118ecb1 827c589
-Author: Bob Zeleznik <zzzman@gmail.com>
-Date: Wed Jun 19 22:40:57 2019 -0400
-
- Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
-
-commit 118ecb14ce519bcbade12b3d52e11b22fcc371b3
-Author: Bob Zeleznik <zzzman@gmail.com>
-Date: Wed Jun 19 22:40:54 2019 -0400
-
- cleaned up and enhanced tree view
-
-commit c5e401cb0a7fec2279ceecbc8d1429dcdd2f04b9
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Wed Jun 19 22:27:21 2019 -0400
-
- buttons on cut links functional except for when dragged from link menu
-
-commit 6fc6054dc7aea144fd967a8cb3fe7d8fe5ec6d6d
-Author: Mohammad Amoush <mohammad_amoush@brown.edu>
-Date: Wed Jun 19 19:13:30 2019 -0400
-
- Width of the presentations fixed, removal of presentations option added, backUP group and normal groups updated when a doc is removed from presentation by removing it from both
-
-commit 827c58950b649629c84211d41fdd4d041287801e
-Merge: 05e50f2 96c26c5
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Wed Jun 19 18:49:50 2019 -0400
-
- Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web
-
-commit 96c26c57527d443784bde9752551bfa10b3ce4d2
-Author: Bob Zeleznik <zzzman@gmail.com>
-Date: Wed Jun 19 18:34:45 2019 -0400
-
- removed marquee summarizing icon
-
-commit 05e50f27a15e8a02ffb27606c51026d1b85bc677
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Wed Jun 19 17:36:52 2019 -0400
-
- Added basic keyboard controls to context menu
-
-commit fa37e023b88127cb8a6b393a848200361a396fb4
-Merge: 565b27c 5b2a498
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Wed Jun 19 16:21:09 2019 -0400
-
- Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web
-
-commit 565b27cca8953a60067de367cae4c0a99beb3cab
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Wed Jun 19 16:21:03 2019 -0400
-
- started adding selection to context menu
-
-commit 5b2a498aca75bd53ffab61f998218bec546b8154
-Merge: 358437e 39e8a7a
-Author: bob <bcz@cs.brown.edu>
-Date: Wed Jun 19 16:17:21 2019 -0400
-
- Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
-
-commit 358437eeafe42e029ffe27702bde15a3fad54a3b
-Author: bob <bcz@cs.brown.edu>
-Date: Wed Jun 19 16:17:18 2019 -0400
-
- working version of embedded tree view docs.
-
-commit 4c1383e47f2203a00bc7f3d73c209f3149d6a772
-Author: Mohammad Amoush <mohammad_amoush@brown.edu>
-Date: Wed Jun 19 15:53:05 2019 -0400
-
- ...
-
-commit a288a2fd0a30a3a16dd01bc4e12dcf6bc117c766
-Author: Mohammad Amoush <mohammad_amoush@brown.edu>
-Date: Wed Jun 19 15:25:24 2019 -0400
-
- Navigation and Zoom Option For Manual Selection Added and New Presentation TItle Naming Added
-
- Now, You can manually click on navigate or zoom and navigate to that document if current was their index. A way to manually disregard groups, and just navigate to that doc.
-
-commit 39e8a7a365442cdc11024c4de8019184fd0057ac
-Merge: 5b6f13d 9ab4739
-Author: Stanley Yip <33562077+yipstanley@users.noreply.github.com>
-Date: Wed Jun 19 15:05:38 2019 -0400
-
- Merge pull request #163 from browngraphicslab/pdf_fixes
-
- deleting annotations
-
-commit 5b6f13d64e9e38b94df0ae61ffedcb0b34290045
-Merge: 35e73f3 4ebbdd8
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Wed Jun 19 15:04:46 2019 -0400
-
- Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web
-
-commit 35e73f369a2145d8a042e0011a43e71763d57998
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Wed Jun 19 15:02:48 2019 -0400
-
- added better search to context menu
-
-commit 9ab47393a2ce3d174ad3238422c2c310764be9af
-Author: yipstanley <stanley_yip@brown.edu>
-Date: Wed Jun 19 14:40:28 2019 -0400
-
- interaction improvements with delete button
-
-commit b9849810231e540a5898a56012abd32c197b23b5
-Author: yipstanley <stanley_yip@brown.edu>
-Date: Wed Jun 19 14:39:15 2019 -0400
-
- anna
-
-commit b960a876d6a31b3eaebb0ac6eca6f191a0d4c900
-Author: yipstanley <stanley_yip@brown.edu>
-Date: Wed Jun 19 14:38:43 2019 -0400
-
- oop
-
-commit 46d57bc21cda4703855b85a4603bd471975d845b
-Author: yipstanley <stanley_yip@brown.edu>
-Date: Wed Jun 19 14:25:47 2019 -0400
-
- deleting annotations
-
-commit f362dbfc237536c6c4a8c6d088c3dc818080f7c2
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Wed Jun 19 12:50:58 2019 -0400
-
- both tail ends of a cut link appear on hover/focus of an anchor
-
-commit fb62f3b2e39bbe2dd3da5eaffedbaa8e60f06dbb
-Author: Mohammad Amoush <mohammad_amoush@brown.edu>
-Date: Wed Jun 19 12:35:54 2019 -0400
-
- Grouping for different presentations fixed
-
-commit 4ebbdd803cdf83806902509dfa0432ce3a139403
-Merge: 0bb2052 c056ade
-Author: Stanley Yip <33562077+yipstanley@users.noreply.github.com>
-Date: Wed Jun 19 11:48:16 2019 -0400
-
- Merge pull request #162 from browngraphicslab/pdf_fixes
-
- Pdf fixes
-
-commit c056adeca11f35972b5f75c6b1cc31292d5765d4
-Author: yipstanley <stanley_yip@brown.edu>
-Date: Wed Jun 19 11:47:20 2019 -0400
-
- push
-
-commit 37f327ab659e6fa1221f9f4ed7649402c5dedc00
-Author: yipstanley <stanley_yip@brown.edu>
-Date: Wed Jun 19 11:24:32 2019 -0400
-
- aspect ratio, dragging, and full screen scrolling fixed
-
-commit 0bb20528c8167b3ba1c4c88d97586d50ae183b4c
-Author: bob <bcz@cs.brown.edu>
-Date: Wed Jun 19 10:37:36 2019 -0400
-
- added highlight for expanded tree view items
-
-commit f60398d5db9041e09c809c16a0b885936ac11a3d
-Author: bob <bcz@cs.brown.edu>
-Date: Wed Jun 19 10:21:37 2019 -0400
-
- fixed multi-column stacking
-
-commit 0674331f3611d297028526c888c718a75b012e0a
-Author: bob <bcz@cs.brown.edu>
-Date: Wed Jun 19 09:36:21 2019 -0400
-
- fixed resizing stacking views. changed defaults for new docs in treeView
-
-commit 1472d2b56aa64896f0a93f172322121d19cd1592
-Author: bob <bcz@cs.brown.edu>
-Date: Wed Jun 19 09:11:35 2019 -0400
-
- fixed lint errors.
-
-commit 8c94bb92b23dea138fa752929b6134e7214dfb60
-Merge: 3b880d7 13e301d
-Author: Bob Zeleznik <zzzman@gmail.com>
-Date: Tue Jun 18 22:51:48 2019 -0400
-
- Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
-
-commit 3b880d7b15b7107049ae27601b9f759b17f7fde9
-Author: Bob Zeleznik <zzzman@gmail.com>
-Date: Tue Jun 18 22:51:46 2019 -0400
-
- added initial keyboard shortcuts for adding and moving docs in TreeView. fixed image drag bug.
-
-commit 13e301dea2f537b67b338cc6a98d3f3b5a8e1f36
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Tue Jun 18 20:58:32 2019 -0400
-
- Fixed linter errors
-
-commit 464fa03d6ebb2a7aaef1d7622afa3e1e7ee816a3
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Tue Jun 18 20:11:31 2019 -0400
-
- Context menu improvements and error fixes
-
-commit 4ffcff69a2fc767c6a03d46d7296b6a8c7ffd281
-Author: madelinegr <mgriswold99@gmail.com>
-Date: Tue Jun 18 19:13:45 2019 -0400
-
- Presentations Listed, Option to Change Added, and
-
-commit ca126adda9e4def83fb5c2e07e382917ca0b4ee0
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Tue Jun 18 17:24:59 2019 -0400
-
- Fixed docking view?
-
-commit b0ac30172019713e1c75083c1199485d902e0eed
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Tue Jun 18 16:37:28 2019 -0400
-
- Fixed zoomBasis stuff and added deletion handling for reponse from server
-
-commit 8e5afb5bbb47324a381b5184254e77eba7bd8536
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Tue Jun 18 16:30:24 2019 -0400
-
- can click on button link to node in different context than source
-
-commit 6fcd0d8d6fb1471b8af460f6d80bdf0d0e681566
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Tue Jun 18 15:17:27 2019 -0400
-
- added button to delete a link
-
-commit d91e7eec9a62363b383b929166cdf600b124334c
-Author: Fawn <fangrui_tong@brown.edu>
-Date: Tue Jun 18 15:09:21 2019 -0400
-
- links to nodes in different contexts render as a circle
-
-commit d3cad099d49690810166d0342f7c371bda0f007e
-Merge: 04668e2 b1af251
-Author: bob <bcz@cs.brown.edu>
-Date: Tue Jun 18 13:30:55 2019 -0400
-
- Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
-
-commit 04668e21313f6e62e5ab35ac737fc54191769a5a
-Author: bob <bcz@cs.brown.edu>
-Date: Tue Jun 18 13:30:41 2019 -0400
-
- fixed cleanup of marquee keyhandler.
-
-commit b1af251b058743798aa3fa3895d22327c8560dfc
-Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com>
-Date: Tue Jun 18 13:19:50 2019 -0400
-
- Added pointer down flag for tab focus
-
-commit 9544576ec0167d64f564ae4c87d392eba07ff467
-Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com>
-Date: Tue Jun 18 13:18:34 2019 -0400
-
- Added tab focusing on hover
-
-commit 2633f61d311528e62d50d4ff56f5884b3b51ac61
-Author: bob <bcz@cs.brown.edu>
-Date: Tue Jun 18 13:12:15 2019 -0400
-
- added undo/redo bindings for app.
-
-commit 3a25bad918c72f5d6de9a720de9e0d316c00f2fe
-Author: bob <bcz@cs.brown.edu>
-Date: Tue Jun 18 13:03:28 2019 -0400
-
- fixed issues with expanding text boxes that have a dynamic title
-
-commit f4fcf306e2579b7479610899a01c06fb157d47de
-Author: bob <bcz@cs.brown.edu>
-Date: Tue Jun 18 12:03:14 2019 -0400
-
- fixed goldenlayout nesting
-
-commit 4f0086f6ea948c1c5254db2acc93f6735987daa5
-Merge: 749eef1 d7ebe7b
-Author: bob <bcz@cs.brown.edu>
-Date: Tue Jun 18 11:31:49 2019 -0400
-
- Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
-
-commit 749eef13af1338225b2bec4dbcd7a50a5650d285
-Author: bob <bcz@cs.brown.edu>
-Date: Tue Jun 18 11:31:46 2019 -0400
-
- fixed image drag drop when not selected.
-
-commit d7ebe7b7d19cf7dc797443aa485293670c3ee4e2
-Merge: 66d4cc9 08872de
-Author: yipstanley <stanley_yip@brown.edu>
-Date: Tue Jun 18 11:08:44 2019 -0400
-
- Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
-
-commit 66d4cc94bcc69f590d90dd35823f93b8e2fb90d8
-Author: yipstanley <stanley_yip@brown.edu>
-Date: Tue Jun 18 10:52:10 2019 -0400
-
- selection fixes
-
-commit 08872def596af073c5f14336c8faf07f44561bbc
-Merge: 8d00265 c50ba1c
-Author: bob <bcz@cs.brown.edu>
-Date: Tue Jun 18 10:28:31 2019 -0400
-
- Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
-
-commit 8d0026573ad9a196f864490bcf07c78f54082bad
-Author: bob <bcz@cs.brown.edu>
-Date: Tue Jun 18 10:28:29 2019 -0400
-
- fixed selection within multicolumn stacking view. added drop of html image selections.
-
-commit c50ba1c4cc01d5cd085dee0dae6f633164efeb80
-Merge: cc032e2 64e6a94
-Author: yipstanley <stanley_yip@brown.edu>
-Date: Tue Jun 18 10:10:58 2019 -0400
-
- Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
-
-commit cc032e2f60015728f64f46ef009c9306e36746a0
-Author: yipstanley <stanley_yip@brown.edu>
-Date: Tue Jun 18 10:05:49 2019 -0400
-
- fixes
-
-commit 64e6a941639aab8d7109178aa151a50909547309
-Author: Bob Zeleznik <zzzman@gmail.com>
-Date: Tue Jun 18 09:05:41 2019 -0400
-
- fixed index out of range
-
-commit 4b8324fcf44c5d3c3a4b3f6e98a4d1dfce84811b
-Author: Bob Zeleznik <zzzman@gmail.com>
-Date: Tue Jun 18 08:53:01 2019 -0400
-
- removed trace
-
-commit a3b8a57027d7c45ea19d259e1ec18fa6a8648c24
-Author: Bob Zeleznik <zzzman@gmail.com>
-Date: Tue Jun 18 08:49:02 2019 -0400
-
- looked like wrong code...
-
-commit 2f5c38c6a0a5220c2a31931c34d94e199854d703
-Author: Bob Zeleznik <zzzman@gmail.com>
-Date: Tue Jun 18 08:36:37 2019 -0400
-
- more streamlining
-
-commit 62c781c0c79ac395c5e117d208a90485ff1ba599
-Author: Bob Zeleznik <zzzman@gmail.com>
-Date: Tue Jun 18 02:19:07 2019 -0400
-
- faster loading of PDFs
-
-commit 4dc8c03562a0473becb895824740da487e16e771
-Author: Bob Zeleznik <zzzman@gmail.com>
-Date: Tue Jun 18 00:17:58 2019 -0400
-
- added dropping of Dash urls from gmail
-
-commit 9c7ff72a8ad249c05b672a46e3fbbb69ffca3a2a
-Merge: 8c64ffd 71b1cfb
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Mon Jun 17 23:04:22 2019 -0400
-
- Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web
-
-commit 8c64ffd92e382050bc8727981cf9fb830e4f02a7
-Author: Tyler Schicke <tyler_schicke@brown.edu>
-Date: Mon Jun 17 23:04:07 2019 -0400
-
- Added share with user functionality