aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Utils.ts2
-rw-r--r--src/client/documents/Documents.ts16
-rw-r--r--src/client/northstar/dash-nodes/HistogramBox.scss6
-rw-r--r--src/client/northstar/dash-nodes/HistogramBox.tsx2
-rw-r--r--src/client/util/DragManager.ts10
-rw-r--r--src/client/util/SelectionManager.ts7
-rw-r--r--src/client/util/TooltipTextMenu.scss4
-rw-r--r--src/client/util/TooltipTextMenu.tsx75
-rw-r--r--src/client/views/DocumentDecorations.scss11
-rw-r--r--src/client/views/DocumentDecorations.tsx68
-rw-r--r--src/client/views/Main.tsx15
-rw-r--r--src/client/views/MainOverlayTextBox.tsx2
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx10
-rw-r--r--src/client/views/collections/CollectionDockingView.scss9
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx61
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss148
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx294
-rw-r--r--src/client/views/collections/CollectionSubView.tsx6
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx9
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx8
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss9
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx91
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx1
-rw-r--r--src/client/views/globalCssVariables.scss4
-rw-r--r--src/client/views/globalCssVariables.scss.d.ts4
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx22
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx3
-rw-r--r--src/client/views/nodes/DocumentView.scss17
-rw-r--r--src/client/views/nodes/DocumentView.tsx109
-rw-r--r--src/client/views/nodes/FieldView.tsx9
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx4
-rw-r--r--src/client/views/nodes/IconBox.scss12
-rw-r--r--src/client/views/nodes/IconBox.tsx45
-rw-r--r--src/client/views/nodes/ImageBox.tsx8
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx2
-rw-r--r--src/fields/IconFIeld.ts25
-rw-r--r--src/new_fields/Schema.ts4
-rw-r--r--src/server/Message.ts2
-rw-r--r--src/server/ServerUtil.ts2
39 files changed, 688 insertions, 448 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index dec6245ef..98f75d3b9 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -1,7 +1,7 @@
import v4 = require('uuid/v4');
import v5 = require("uuid/v5");
import { Socket } from 'socket.io';
-import { Message, Types, Transferable } from './server/Message';
+import { Message } from './server/Message';
import { Document } from './fields/Document';
export class Utils {
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4febfa7eb..b0bb74d89 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -32,6 +32,9 @@ import { action } from "mobx";
import { ColumnAttributeModel } from "../northstar/core/attribute/AttributeModel";
import { AttributeTransformationModel } from "../northstar/core/attribute/AttributeTransformationModel";
import { AggregateFunction } from "../northstar/model/idea/idea";
+import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
+import { IconBox } from "../views/nodes/IconBox";
+import { IconField } from "../../fields/IconFIeld";
export interface DocumentOptions {
x?: number;
@@ -63,6 +66,7 @@ export namespace Documents {
let videoProto: Document;
let audioProto: Document;
let pdfProto: Document;
+ let iconProto: Document;
const textProtoId = "textProto";
const histoProtoId = "histoProto";
const pdfProtoId = "pdfProto";
@@ -72,6 +76,7 @@ export namespace Documents {
const kvpProtoId = "kvpProto";
const videoProtoId = "videoProto";
const audioProtoId = "audioProto";
+ const iconProtoId = "iconProto";
export function initProtos(): Promise<void> {
return Server.GetFields([textProtoId, histoProtoId, collProtoId, pdfProtoId, imageProtoId, videoProtoId, audioProtoId, webProtoId, kvpProtoId]).then(fields => {
@@ -84,6 +89,7 @@ export namespace Documents {
videoProto = fields[videoProtoId] as Document || CreateVideoPrototype();
audioProto = fields[audioProtoId] as Document || CreateAudioPrototype();
pdfProto = fields[pdfProtoId] as Document || CreatePdfPrototype();
+ iconProto = fields[iconProtoId] as Document || CreateIconPrototype();
});
}
function assignOptions(doc: Document, options: DocumentOptions): Document {
@@ -92,6 +98,8 @@ export namespace Documents {
if (options.title !== undefined) { doc.SetText(KeyStore.Title, options.title); }
if (options.page !== undefined) { doc.SetNumber(KeyStore.Page, options.page); }
if (options.scale !== undefined) { doc.SetNumber(KeyStore.Scale, options.scale); }
+ if (options.width !== undefined) { doc.SetNumber(KeyStore.Width, options.width); }
+ if (options.height !== undefined) { doc.SetNumber(KeyStore.Height, options.height); }
if (options.viewType !== undefined) { doc.SetNumber(KeyStore.ViewType, options.viewType); }
if (options.backgroundColor !== undefined) { doc.SetText(KeyStore.BackgroundColor, options.backgroundColor); }
if (options.ink !== undefined) { doc.Set(KeyStore.Ink, new InkField(options.ink)); }
@@ -139,6 +147,11 @@ export namespace Documents {
histoProto.SetText(KeyStore.BackgroundLayout, HistogramBox.LayoutString());
return histoProto;
}
+ function CreateIconPrototype(): Document {
+ let iconProto = setupPrototypeOptions(iconProtoId, "ICON_PROTO", IconBox.LayoutString(),
+ { x: 0, y: 0, width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE), layoutKeys: [KeyStore.Data] });
+ return iconProto;
+ }
function CreateTextPrototype(): Document {
let textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(),
{ x: 0, y: 0, width: 300, height: 150, layoutKeys: [KeyStore.Data] });
@@ -203,6 +216,9 @@ export namespace Documents {
export function TextDocument(options: DocumentOptions = {}) {
return assignToDelegate(SetInstanceOptions(textProto, options, ["", TextField]).MakeDelegate(), options);
}
+ export function IconDocument(icon: string, options: DocumentOptions = {}) {
+ return assignToDelegate(SetInstanceOptions(iconProto, { width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE), layoutKeys: [KeyStore.Data], layout: IconBox.LayoutString(), ...options }, [icon, IconField]), options);
+ }
export function PdfDocument(url: string, options: DocumentOptions = {}) {
return assignToDelegate(SetInstanceOptions(pdfProto, options, [new URL(url), PDFField]).MakeDelegate(), options);
}
diff --git a/src/client/northstar/dash-nodes/HistogramBox.scss b/src/client/northstar/dash-nodes/HistogramBox.scss
index e899cf15e..06d781263 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.scss
+++ b/src/client/northstar/dash-nodes/HistogramBox.scss
@@ -1,12 +1,12 @@
.histogrambox-container {
padding: 0vw;
position: absolute;
- top: 0;
- left:0;
+ top: -50%;
+ left:-50%;
text-align: center;
width: 100%;
height: 100%;
- background: black;
+ background: black;
}
.histogrambox-xaxislabel {
position:absolute;
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 0e84ace50..e2ecc8c83 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -146,7 +146,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
return (
<Measure onResize={(r: any) => runInAction(() => { this.PanelWidth = r.entry.width; this.PanelHeight = r.entry.height; })}>
{({ measureRef }) =>
- <div className="histogrambox-container" ref={measureRef} style={{ transform: `translate(-50%, -50%)` }}>
+ <div className="histogrambox-container" ref={measureRef}>
<div className="histogrambox-yaxislabel" onPointerDown={this.yLabelPointerDown} ref={this._dropYRef} >
<span className="histogrambox-yaxislabel-text">
{labelY}
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 4bd654e15..46658867b 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -250,7 +250,7 @@ export namespace DragManager {
dragData.aliasOnDrop = e.ctrlKey || e.altKey;
}
if (e.shiftKey) {
- abortDrag();
+ AbortDrag();
CollectionDockingView.Instance.StartOtherDrag(docs, {
pageX: e.pageX,
pageY: e.pageY,
@@ -269,20 +269,22 @@ export namespace DragManager {
);
};
- const abortDrag = () => {
+ AbortDrag = () => {
document.removeEventListener("pointermove", moveHandler, true);
document.removeEventListener("pointerup", upHandler);
- dragElements.map(dragElement => dragDiv.removeChild(dragElement));
+ dragElements.map(dragElement => { if (dragElement.parentNode == dragDiv) dragDiv.removeChild(dragElement); });
eles.map(ele => (ele.hidden = false));
};
const upHandler = (e: PointerEvent) => {
- abortDrag();
+ AbortDrag();
FinishDrag(eles, e, dragData, options, finishDrag);
};
document.addEventListener("pointermove", moveHandler, true);
document.addEventListener("pointerup", upHandler);
}
+ export let AbortDrag: () => void = emptyFunction;
+
function FinishDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) {
let removed = dragEles.map(dragEle => {
let parent = dragEle.parentElement;
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 320553952..92d78696e 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -3,6 +3,7 @@ import { DocumentView } from "../views/nodes/DocumentView";
import { Document } from "../../fields/Document";
import { Main } from "../views/Main";
import { MainOverlayTextBox } from "../views/MainOverlayTextBox";
+import { DragManager } from "./DragManager";
export namespace SelectionManager {
class Manager {
@@ -18,13 +19,13 @@ export namespace SelectionManager {
if (manager.SelectedDocuments.indexOf(doc) === -1) {
manager.SelectedDocuments.push(doc);
- doc.props.onActiveChanged(true);
+ doc.props.whenActiveChanged(true);
}
}
@action
DeselectAll(): void {
- manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false));
+ manager.SelectedDocuments.map(dv => dv.props.whenActiveChanged(false));
manager.SelectedDocuments = [];
MainOverlayTextBox.Instance.SetTextDoc();
}
@@ -36,7 +37,7 @@ export namespace SelectionManager {
}
@action
ReselectAll2(sdocs: DocumentView[]) {
- sdocs.map(s => SelectionManager.SelectDoc(s, false));
+ sdocs.map(s => SelectionManager.SelectDoc(s, true));
}
}
diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss
index 5c2d66480..70d9ad772 100644
--- a/src/client/util/TooltipTextMenu.scss
+++ b/src/client/util/TooltipTextMenu.scss
@@ -35,6 +35,10 @@
cursor: pointer;
position: relative;
padding-right: 15px;
+ margin: 3px;
+ background: #333333;
+ border-radius: 3px;
+ text-align: center;
}
.ProseMirror-menu-dropdown-wrap {
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index a92cbd263..4f0eb7d63 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -34,6 +34,9 @@ export class TooltipTextMenu {
private fontSizeToNum: Map<MarkType, number>;
private fontStylesToName: Map<MarkType, string>;
private fontSizeIndicator: HTMLSpanElement = document.createElement("span");
+ //dropdown doms
+ private fontSizeDom: Node;
+ private fontStyleDom: Node;
constructor(view: EditorView, editorProps: FieldViewProps) {
this.view = view;
@@ -76,7 +79,7 @@ export class TooltipTextMenu {
this.fontStylesToName.set(schema.marks.timesNewRoman, "Times New Roman");
this.fontStylesToName.set(schema.marks.arial, "Arial");
this.fontStylesToName.set(schema.marks.georgia, "Georgia");
- this.fontStylesToName.set(schema.marks.comicSans, "Comic Sans");
+ this.fontStylesToName.set(schema.marks.comicSans, "Comic Sans MS");
this.fontStylesToName.set(schema.marks.tahoma, "Tahoma");
this.fontStylesToName.set(schema.marks.impact, "Impact");
this.fontStylesToName.set(schema.marks.crimson, "Crimson Text");
@@ -93,23 +96,15 @@ export class TooltipTextMenu {
this.fontSizeToNum.set(schema.marks.p72, 72);
this.fontSizes = Array.from(this.fontSizeToNum.keys());
- this.addFontDropdowns();
+ //this.addFontDropdowns();
this.update(view, undefined);
}
- //adds font size and font style dropdowns
- addFontDropdowns() {
+ //label of dropdown will change to given label
+ updateFontSizeDropdown(label: string) {
//filtering function - might be unecessary
let cut = (arr: MenuItem[]) => arr.filter(x => x);
- //font STYLES
- let fontBtns: MenuItem[] = [];
- this.fontStylesToName.forEach((name, mark) => {
- fontBtns.push(this.dropdownBtn(name, "font-family: " + name + ", sans-serif; width: 120px;", mark, this.view, this.changeToMarkInGroup, this.fontStyles));
- });
-
- //font size indicator
- this.fontSizeIndicator = this.icon("12", "font-size-indicator");
//font SIZES
let fontSizeBtns: MenuItem[] = [];
@@ -117,14 +112,32 @@ export class TooltipTextMenu {
fontSizeBtns.push(this.dropdownBtn(String(number), "width: 50px;", mark, this.view, this.changeToMarkInGroup, this.fontSizes));
});
- //dropdown to hold font btns
- let dd_fontStyle = new Dropdown(cut(fontBtns), { label: "Font Style", css: "color:white;" }) as MenuItem;
- let dd_fontSize = new Dropdown(cut(fontSizeBtns), { label: "Font Size", css: "color:white; margin-left: -6px;" }) as MenuItem;
- this.tooltip.appendChild(dd_fontStyle.render(this.view).dom);
- this.tooltip.appendChild(this.fontSizeIndicator);
- this.tooltip.appendChild(dd_fontSize.render(this.view).dom);
- dd_fontStyle.render(this.view).dom.nodeValue = "TEST";
- console.log(dd_fontStyle.render(this.view).dom.nodeValue);
+ if (this.fontSizeDom) { this.tooltip.removeChild(this.fontSizeDom); }
+ this.fontSizeDom = (new Dropdown(cut(fontSizeBtns), {
+ label: label,
+ css: "color:white; min-width: 60px; padding-left: 5px; margin-right: 0;"
+ }) as MenuItem).render(this.view).dom;
+ this.tooltip.appendChild(this.fontSizeDom);
+ }
+
+ //label of dropdown will change to given label
+ updateFontStyleDropdown(label: string) {
+ //filtering function - might be unecessary
+ let cut = (arr: MenuItem[]) => arr.filter(x => x);
+
+ //font STYLES
+ let fontBtns: MenuItem[] = [];
+ this.fontStylesToName.forEach((name, mark) => {
+ fontBtns.push(this.dropdownBtn(name, "font-family: " + name + ", sans-serif; width: 125px;", mark, this.view, this.changeToMarkInGroup, this.fontStyles));
+ });
+
+ if (this.fontStyleDom) { this.tooltip.removeChild(this.fontStyleDom); }
+ this.fontStyleDom = (new Dropdown(cut(fontBtns), {
+ label: label,
+ css: "color:white; width: 125px; margin-left: -3px; padding-left: 2px;"
+ }) as MenuItem).render(this.view).dom;
+
+ this.tooltip.appendChild(this.fontStyleDom);
}
//for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text
@@ -246,30 +259,32 @@ export class TooltipTextMenu {
let width = Math.abs(start.left - end.left) / 2 * this.editorProps.ScreenToLocalTransform().Scale;
let mid = Math.min(start.left, end.left) + width;
- this.tooltip.style.width = 220 + "px";
+ this.tooltip.style.width = 225 + "px";
this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px";
+ //UPDATE FONT STYLE DROPDOWN
let activeStyles = this.activeMarksOnSelection(this.fontStyles);
if (activeStyles.length === 1) {
// if we want to update something somewhere with active font name
let fontName = this.fontStylesToName.get(activeStyles[0]);
+ if (fontName) { this.updateFontStyleDropdown(fontName); }
} else if (activeStyles.length === 0) {
//crimson on default
+ this.updateFontStyleDropdown("Crimson Text");
+ } else {
+ this.updateFontStyleDropdown("Various");
}
- //update font size indicator
+ //UPDATE FONT SIZE DROPDOWN
let activeSizes = this.activeMarksOnSelection(this.fontSizes);
if (activeSizes.length === 1) { //if there's only one active font size
let size = this.fontSizeToNum.get(activeSizes[0]);
- if (size) {
- this.fontSizeIndicator.innerHTML = String(size);
- }
- //should be 14 on default
+ if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
} else if (activeSizes.length === 0) {
- this.fontSizeIndicator.innerHTML = "14";
- //multiple font sizes selected
- } else {
- this.fontSizeIndicator.innerHTML = "";
+ //should be 14 on default
+ this.updateFontSizeDropdown("14 pt");
+ } else { //multiple font sizes selected
+ this.updateFontSizeDropdown("Various");
}
}
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index c1a949639..f78bf9ff8 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -3,14 +3,14 @@
.documentDecorations {
position: absolute;
}
-#documentDecorations-container {
+.documentDecorations-container {
+ z-index: $docDecorations-zindex;
position: absolute;
top: 0;
left:0;
display: grid;
- z-index: $docDecorations-zindex;
grid-template-rows: 20px 8px 1fr 8px;
- grid-template-columns: 8px 8px 1fr 8px 8px;
+ grid-template-columns: 8px 16px 1fr 8px 8px;
pointer-events: none;
#documentDecorations-centerCont {
@@ -88,6 +88,11 @@
pointer-events: all;
text-align: center;
cursor: pointer;
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ width: $MINIMIZED_ICON_SIZE;
+ height: $MINIMIZED_ICON_SIZE;
}
.documentDecorations-background {
background: lightblue;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 16fac0694..1f5078c85 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -5,6 +5,7 @@ import { KeyStore } from "../../fields/KeyStore";
import { ListField } from "../../fields/ListField";
import { NumberField } from "../../fields/NumberField";
import { TextField } from "../../fields/TextField";
+import { Document } from "../../fields/Document";
import { emptyFunction } from "../../Utils";
import { DragLinksAsDocuments, DragManager } from "../util/DragManager";
import { SelectionManager } from "../util/SelectionManager";
@@ -14,6 +15,8 @@ import { MainOverlayTextBox } from "./MainOverlayTextBox";
import { DocumentView } from "./nodes/DocumentView";
import { LinkMenu } from "./nodes/LinkMenu";
import React = require("react");
+import { CompileScript } from "../util/Scripting";
+import { IconBox } from "./nodes/IconBox";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -36,6 +39,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
@observable private _hidden = false;
@observable private _opacity = 1;
@observable private _dragging = false;
+ @observable private _iconifying = false;
constructor(props: Readonly<{}>) {
@@ -175,25 +179,65 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
document.removeEventListener("pointerup", this.onCloseUp);
}
}
+ _downX = 0;
+ _downY = 0;
onMinimizeDown = (e: React.PointerEvent): void => {
e.stopPropagation();
if (e.button === 0) {
+ this._downX = e.pageX;
+ this._downY = e.pageY;
document.removeEventListener("pointermove", this.onMinimizeMove);
document.addEventListener("pointermove", this.onMinimizeMove);
document.removeEventListener("pointerup", this.onMinimizeUp);
document.addEventListener("pointerup", this.onMinimizeUp);
}
}
+
+ @observable _minimizedX = 0;
+ @observable _minimizedY = 0;
+ @action
onMinimizeMove = (e: PointerEvent): void => {
e.stopPropagation();
+ let dx = e.pageX - this._downX;
+ let dy = e.pageY - this._downY;
+ if (Math.abs(dx) > 4 || Math.abs(dy) > 4) {
+ this._iconifying = true;
+ let xf = SelectionManager.SelectedDocuments()[0].props.ScreenToLocalTransform().scale(SelectionManager.SelectedDocuments()[0].props.ContentScaling()).inverse().transformPoint(0, 0);
+ let dx = e.pageX - xf[0];
+ let dy = e.pageY - xf[1];
+ this._minimizedX = e.clientX;
+ this._minimizedY = e.clientY;
+ if (Math.abs(dx) < 20 && Math.abs(dy) < 20) {
+ this._minimizedX = xf[0];
+ this._minimizedY = xf[1];
+ }
+ SelectionManager.SelectedDocuments().map(dv => {
+ let minDoc = dv.props.Document.Get(KeyStore.MinimizedDoc);
+ if (minDoc instanceof Document) {
+ let where = (dv.props.ScreenToLocalTransform()).scale(dv.props.ContentScaling()).transformPoint(this._minimizedX, this._minimizedY);
+ let minDocument = minDoc as Document;
+ minDocument.SetNumber(KeyStore.X, where[0] + dv.props.Document.GetNumber(KeyStore.X, 0));
+ minDocument.SetNumber(KeyStore.Y, where[1] + dv.props.Document.GetNumber(KeyStore.Y, 0));
+ }
+ });
+ }
}
+ @action
onMinimizeUp = (e: PointerEvent): void => {
e.stopPropagation();
if (e.button === 0) {
- SelectionManager.SelectedDocuments().map(dv => dv.minimize());
+ let dx = e.clientX - this._downX;
+ let dy = e.clientY - this._downY;
+ if (Math.abs(dx) < 4 && Math.abs(dy) < 4 && !this._iconifying) {
+ SelectionManager.SelectedDocuments().map(dv => dv.minimize());
+ SelectionManager.DeselectAll();
+ } else {
+ this._minimizedX = this._minimizedY = 0;
+ }
document.removeEventListener("pointermove", this.onMinimizeMove);
document.removeEventListener("pointerup", this.onMinimizeUp);
}
+ this._iconifying = false;
}
onPointerDown = (e: React.PointerEvent): void => {
@@ -370,11 +414,22 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
// }
render() {
var bounds = this.Bounds;
- if (bounds.x === Number.MAX_VALUE) {
+ let seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
+ if (bounds.x === Number.MAX_VALUE || !seldoc) {
return (null);
}
- // console.log(this._documents.length)
- // let test = this._documents[0].props.Document.Title;
+ let selpos = this._minimizedX !== 0 || this._minimizedY !== 0 ?
+ [this._minimizedX - 12 + (!this._iconifying ? 8 : 0), this._minimizedY - 12 + (!this._iconifying ? 28 : 0)] :
+ [0, this._iconifying ? -18 : 0];
+ let minimizeIcon = (
+ <div className="documentDecorations-minimizeButton" onPointerDown={this.onMinimizeDown}
+ style={{ transform: `translate(${selpos[0]}px,${selpos[1]}px)`, }}>
+ {SelectionManager.SelectedDocuments().length == 1 ? IconBox.DocumentIcon(SelectionManager.SelectedDocuments()[0].props.Document.GetText(KeyStore.Layout, "...")) : "..."}
+ </div>);
+ if (this._iconifying) {
+ return (<div className="documentDecorations-container" > {minimizeIcon} </div>);
+ }
+
if (this.Hidden) {
return (null);
}
@@ -406,14 +461,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
zIndex: SelectionManager.SelectedDocuments().length > 1 ? 1000 : 0,
}} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); }} >
</div>
- <div id="documentDecorations-container" style={{
+ <div className="documentDecorations-container" style={{
width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
height: (bounds.b - bounds.y + this._resizeBorderWidth + this._linkBoxHeight + this._titleHeight) + "px",
left: bounds.x - this._resizeBorderWidth / 2,
top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
opacity: this._opacity
}}>
- <div className="documentDecorations-minimizeButton" onPointerDown={this.onMinimizeDown}>...</div>
+ {minimizeIcon}
+
<input ref={this.keyinput} className="title" type="text" name="dynbox" value={this.getValue()} onChange={this.handleChange} onPointerDown={this.onBackgroundDown} onKeyPress={this.enterPressed} />
<div className="documentDecorations-closeButton" onPointerDown={this.onCloseDown}>X</div>
<div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 0469211fa..09ef30f6b 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -27,7 +27,7 @@ import '../northstar/model/ModelExtensions';
import { HistogramOperation } from '../northstar/operations/HistogramOperation';
import '../northstar/utils/Extensions';
import { Server } from '../Server';
-import { SetupDrag } from '../util/DragManager';
+import { SetupDrag, DragManager } from '../util/DragManager';
import { Transform } from '../util/Transform';
import { UndoManager } from '../util/UndoManager';
import { CollectionDockingView } from './collections/CollectionDockingView';
@@ -38,6 +38,7 @@ import "./Main.scss";
import { MainOverlayTextBox } from './MainOverlayTextBox';
import { DocumentView } from './nodes/DocumentView';
import { PreviewCursor } from './PreviewCursor';
+import { SelectionManager } from '../util/SelectionManager';
@observer
@@ -111,6 +112,12 @@ export class Main extends React.Component {
// window.addEventListener("pointermove", (e) => this.reportLocation(e))
window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler
window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler
+ window.addEventListener("keydown", (e) => {
+ if (e.key == "Escape") {
+ DragManager.AbortDrag();
+ SelectionManager.DeselectAll()
+ }
+ }, false); // drag event handler
// click interactions for the context menu
document.addEventListener("pointerdown", action(function (e: PointerEvent) {
if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) {
@@ -190,7 +197,7 @@ export class Main extends React.Component {
selectOnLoad={false}
focus={emptyDocFunction}
parentActive={returnTrue}
- onActiveChanged={emptyFunction}
+ whenActiveChanged={emptyFunction}
ContainingCollectionView={undefined} />}
</div>
}
@@ -210,9 +217,9 @@ export class Main extends React.Component {
let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" }));
let addSchemaNode = action(() => Documents.SchemaDocument([], { width: 200, height: 200, title: "a schema collection" }));
let addTreeNode = action(() => Documents.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", copyDraggedItems: true }));
- let addVideoNode = action(() => Documents.VideoDocument(videourl, { width: 200, height: 200, title: "video node" }));
+ let addVideoNode = action(() => Documents.VideoDocument(videourl, { width: 200, title: "video node" }));
let addPDFNode = action(() => Documents.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" }));
- let addImageNode = action(() => Documents.ImageDocument(imgurl, { width: 200, height: 200, title: "an image of a cat" }));
+ let addImageNode = action(() => Documents.ImageDocument(imgurl, { width: 200, title: "an image of a cat" }));
let addWebNode = action(() => Documents.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" }));
let addAudioNode = action(() => Documents.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" }));
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 141b3ad74..8cb01117c 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -101,7 +101,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
return <div className="mainOverlayTextBox-textInput" style={{ pointerEvents: "none", transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
<div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ pointerEvents: "none", transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
<FormattedTextBox fieldKey={this._textFieldKey} isOverlay={true} Document={this.TextDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
- selectOnLoad={true} ContainingCollectionView={undefined} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} />
+ selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} />
</div>
</ div>;
}
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 4fda38a26..4755b2d57 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -22,7 +22,7 @@ export interface CollectionRenderProps {
removeDocument: (document: Document) => boolean;
moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
active: () => boolean;
- onActiveChanged: (isActive: boolean) => void;
+ whenActiveChanged: (isActive: boolean) => void;
}
export interface CollectionViewProps extends FieldViewProps {
@@ -55,9 +55,9 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
//TODO should this be observable?
private _isChildActive = false;
- onActiveChanged = (isActive: boolean) => {
+ whenActiveChanged = (isActive: boolean) => {
this._isChildActive = isActive;
- this.props.onActiveChanged(isActive);
+ this.props.whenActiveChanged(isActive);
}
createsCycle(documentToAdd: Document, containerDocument: Document): boolean {
@@ -90,7 +90,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
let props = this.props;
var curPage = props.Document.GetNumber(KeyStore.CurPage, -1);
doc.SetOnPrototype(KeyStore.Page, new NumberField(curPage));
- if (this.isAnnotationOverlay) {
+ if (true || this.isAnnotationOverlay) {
doc.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1));
}
if (curPage >= 0) {
@@ -184,7 +184,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
removeDocument: this.removeDocument,
moveDocument: this.moveDocument,
active: this.active,
- onActiveChanged: this.onActiveChanged,
+ whenActiveChanged: this.whenActiveChanged,
};
const viewtype = this.collectionViewType;
return (
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 50da2b11d..0e7e0afa7 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -2,15 +2,6 @@
.collectiondockingview-content {
height: 100%;
- text-align:center;
- .documentView-node-topmost {
- text-align:left;
- transform-origin: center top;
- display: inline-block;
- }
-}
-.collectiondockingview-content-height {
- height: 100%;
}
.lm_active .messageCounter{
color:white;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 2b886adb6..e4c647635 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -19,7 +19,7 @@ import { ServerUtils } from "../../../server/ServerUtil";
import { DragManager, DragLinksAsDocuments } from "../../util/DragManager";
import { TextField } from "../../../fields/TextField";
import { ListField } from "../../../fields/ListField";
-import { thisExpression } from "babel-types";
+import { Transform } from '../../util/Transform'
@observer
export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
@@ -316,7 +316,7 @@ interface DockedFrameProps {
@observer
export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
- private _mainCont = React.createRef<HTMLDivElement>();
+ _mainCont = React.createRef<HTMLDivElement>();
@observable private _panelWidth = 0;
@observable private _panelHeight = 0;
@observable private _document: Opt<Document>;
@@ -326,50 +326,49 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
Server.GetField(this.props.documentId, action((f: Opt<Field>) => this._document = f as Document));
}
- private _nativeWidth = () => this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth);
- private _nativeHeight = () => this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight);
- private _contentScaling = () => {
- let wscale = this._panelWidth / (this._nativeWidth() ? this._nativeWidth() : this._panelWidth);
- if (wscale * this._nativeHeight() > this._panelHeight)
- return this._panelHeight / (this._nativeHeight() ? this._nativeHeight() : this._panelHeight);
+ nativeWidth = () => this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth);
+ nativeHeight = () => this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight);
+ contentScaling = () => {
+ let wscale = this._panelWidth / (this.nativeWidth() ? this.nativeWidth() : this._panelWidth);
+ if (wscale * this.nativeHeight() > this._panelHeight)
+ return this._panelHeight / (this.nativeHeight() ? this.nativeHeight() : this._panelHeight);
return wscale;
}
ScreenToLocalTransform = () => {
- let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!.children[0].firstChild as HTMLElement);
- let scaling = scale;
- {
- let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!);
- scaling = scale;
+ if (this._mainCont.current && this._mainCont.current.children) {
+ let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!.children[0].firstChild as HTMLElement);
+ scale = Utils.GetScreenTransform(this._mainCont.current!).scale;
+ return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this.contentScaling());
}
- return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scaling / this._contentScaling());
+ return Transform.Identity();
}
+ get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; }
- render() {
- if (!this._document) {
- return (null);
- }
- let wscale = this._panelWidth / (this._nativeWidth() ? this._nativeWidth() : this._panelWidth);
- let name = (wscale * this._nativeHeight() > this._panelHeight) ? "" : "-height";
- var content =
- <div className={`collectionDockingView-content${name}`} ref={this._mainCont}>
- <DocumentView key={this._document.Id} Document={this._document}
+ get content() {
+ return (
+ <div className="collectionDockingView-content" ref={this._mainCont}
+ style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
+ <DocumentView key={this._document!.Id} Document={this._document!}
addDocument={undefined}
removeDocument={undefined}
- ContentScaling={this._contentScaling}
- PanelWidth={this._nativeWidth}
- PanelHeight={this._nativeHeight}
+ ContentScaling={this.contentScaling}
+ PanelWidth={this.nativeWidth}
+ PanelHeight={this.nativeHeight}
ScreenToLocalTransform={this.ScreenToLocalTransform}
isTopMost={true}
selectOnLoad={false}
parentActive={returnTrue}
- onActiveChanged={emptyFunction}
+ whenActiveChanged={emptyFunction}
focus={emptyDocFunction}
ContainingCollectionView={undefined} />
- </div>;
+ </div>);
+ }
- return <Measure onResize={action((r: any) => { this._panelWidth = r.entry.width; this._panelHeight = r.entry.height; })}>
- {({ measureRef }) => <div ref={measureRef}> {content} </div>}
- </Measure>;
+ render() {
+ return !this._document ? (null) :
+ <Measure onResize={action((r: any) => { this._panelWidth = r.entry.width; this._panelHeight = r.entry.height; })}>
+ {({ measureRef }) => <div ref={measureRef}> {this.content} </div>}
+ </Measure>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index c8bfedff4..cfdb3ab22 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -1,61 +1,6 @@
@import "../globalCssVariables";
-//options menu styling
-#schemaOptionsMenuBtn {
- position: absolute;
- height: 20px;
- width: 20px;
- border-radius: 50%;
- z-index: 21;
- right: 4px;
- top: 4px;
- pointer-events: auto;
- background-color:black;
- display:inline-block;
- padding: 0px;
- font-size: 100%;
-}
-
-ul {
- list-style-type: disc;
-}
-
-#schema-options-header {
- text-align: center;
- padding: 0px;
- margin: 0px;
-}
-.schema-options-subHeader {
- color: $intermediate-color;
- margin-bottom: 5px;
-}
-#schemaOptionsMenuBtn:hover {
- transform: scale(1.15);
-}
-
-#preview-schema-checkbox-div {
- margin-left: 20px;
- font-size: 12px;
-}
- #options-flyout-div {
- text-align: left;
- padding:0px;
- z-index: 100;
- font-family: $sans-serif;
- padding-left: 5px;
- }
-
- #schema-col-checklist {
- overflow: scroll;
- text-align: left;
- //background-color: $light-color-secondary;
- line-height: 25px;
- max-height: 175px;
- font-family: $sans-serif;
- font-size: 12px;
- }
-
.collectionSchemaView-container {
border-width: $COLLECTION_BORDER_WIDTH;
@@ -66,18 +11,31 @@ ul {
position: absolute;
width: 100%;
height: 100%;
-
- .collectionSchemaView-content {
- position: absolute;
- height: 100%;
- width: 100%;
- overflow: auto;
+
+ .collectionSchemaView-cellContents {
+ height: $MAX_ROW_HEIGHT;
}
+
.collectionSchemaView-previewRegion {
position: relative;
background: $light-color;
float: left;
height: 100%;
+ .collectionSchemaView-previewDoc {
+ height: 100%;
+ width: 100%;
+ position: absolute;
+ }
+ .collectionSchemaView-input {
+ position: absolute;
+ max-width: 150px;
+ width: 100%;
+ bottom: 0px;
+ }
+ .documentView-node:first-child {
+ position: relative;
+ background: $light-color;
+ }
}
.collectionSchemaView-previewHandle {
position: absolute;
@@ -151,7 +109,7 @@ ul {
}
.rt-tr-group {
direction: ltr;
- max-height: 44px;
+ max-height: $MAX_ROW_HEIGHT;
}
.rt-td {
border-width: 1px;
@@ -183,7 +141,7 @@ ul {
}
.ReactTable .rt-th,
.ReactTable .rt-td {
- max-height: 44;
+ max-height: $MAX_ROW_HEIGHT;
padding: 3px 7px;
font-size: 13px;
text-align: center;
@@ -193,13 +151,71 @@ ul {
border-bottom-style: solid;
border-bottom-width: 1;
}
+ .documentView-node-topmost {
+ text-align:left;
+ transform-origin: center top;
+ display: inline-block;
+ }
.documentView-node:first-child {
background: $light-color;
- .imageBox-cont img {
- object-fit: contain;
- }
}
}
+//options menu styling
+#schemaOptionsMenuBtn {
+ position: absolute;
+ height: 20px;
+ width: 20px;
+ border-radius: 50%;
+ z-index: 21;
+ right: 4px;
+ top: 4px;
+ pointer-events: auto;
+ background-color:black;
+ display:inline-block;
+ padding: 0px;
+ font-size: 100%;
+}
+
+ul {
+ list-style-type: disc;
+}
+
+#schema-options-header {
+ text-align: center;
+ padding: 0px;
+ margin: 0px;
+}
+.schema-options-subHeader {
+ color: $intermediate-color;
+ margin-bottom: 5px;
+}
+#schemaOptionsMenuBtn:hover {
+ transform: scale(1.15);
+}
+
+#preview-schema-checkbox-div {
+ margin-left: 20px;
+ font-size: 12px;
+}
+
+ #options-flyout-div {
+ text-align: left;
+ padding:0px;
+ z-index: 100;
+ font-family: $sans-serif;
+ padding-left: 5px;
+ }
+
+ #schema-col-checklist {
+ overflow: scroll;
+ text-align: left;
+ //background-color: $light-color-secondary;
+ line-height: 25px;
+ max-height: 175px;
+ font-family: $sans-serif;
+ font-size: 12px;
+ }
+
.Resizer {
box-sizing: border-box;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 1defdba7e..90077b053 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -4,15 +4,15 @@ import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, untracked } from "mobx";
import { observer } from "mobx-react";
-import Measure from "react-measure";
import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
+import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'
import "react-table/react-table.css";
import { Document } from "../../../fields/Document";
import { Field, Opt } from "../../../fields/Field";
import { Key } from "../../../fields/Key";
import { KeyStore } from "../../../fields/KeyStore";
import { ListField } from "../../../fields/ListField";
-import { emptyDocFunction, emptyFunction, returnFalse, returnOne } from "../../../Utils";
+import { emptyDocFunction, emptyFunction, returnFalse } from "../../../Utils";
import { Server } from "../../Server";
import { SetupDrag } from "../../util/DragManager";
import { CompileScript, ToField } from "../../util/Scripting";
@@ -34,42 +34,35 @@ import { CollectionSubView } from "./CollectionSubView";
class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggle: (key: Key) => void }> {
@observable key: Key | undefined;
- componentWillReceiveProps() {
- Server.GetField(this.props.keyId, action((field: Opt<Field>) => {
- if (field instanceof Key) {
- this.key = field;
- }
- }));
+ constructor(props: any) {
+ super(props);
+ Server.GetField(this.props.keyId, action((field: Opt<Field>) => field instanceof Key && (this.key = field)));
}
render() {
- if (this.key) {
- return (<div key={this.key.Id}>
+ return !this.key ? (null) :
+ (<div key={this.key.Id}>
<input type="checkbox" checked={this.props.checked} onChange={() => this.key && this.props.toggle(this.key)} />
{this.key.Name}
</div>);
- }
- return (null);
}
}
@observer
export class CollectionSchemaView extends CollectionSubView {
- private _mainCont = React.createRef<HTMLDivElement>();
+ private _mainCont?: HTMLDivElement;
private _startSplitPercent = 0;
private DIVIDER_WIDTH = 4;
@observable _columns: Array<Key> = [KeyStore.Title, KeyStore.Data, KeyStore.Author];
- @observable _contentScaling = 1; // used to transfer the dimensions of the content pane in the DOM to the ContentScaling prop of the DocumentView
- @observable _dividerX = 0;
- @observable _panelWidth = 0;
- @observable _panelHeight = 0;
@observable _selectedIndex = 0;
@observable _columnsPercentage = 0;
@observable _keys: Key[] = [];
+ @observable _newKeyName: string = "";
@computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); }
-
+ @computed get columns() { return this.props.Document.GetList(KeyStore.ColumnsKey, [] as Key[]); }
+ @computed get borderWidth() { return COLLECTION_BORDER_WIDTH; }
renderCell = (rowProps: CellInfo) => {
let props: FieldViewProps = {
@@ -83,7 +76,7 @@ export class CollectionSchemaView extends CollectionSubView {
ScreenToLocalTransform: Transform.Identity,
focus: emptyDocFunction,
active: returnFalse,
- onActiveChanged: emptyFunction,
+ whenActiveChanged: emptyFunction,
};
let contents = (
<FieldView {...props} />
@@ -107,11 +100,11 @@ export class CollectionSchemaView extends CollectionSubView {
return false;
};
return (
- <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "56px" }} key={props.Document.Id} ref={reference}>
+ <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} key={props.Document.Id} ref={reference}>
<EditableView
display={"inline"}
contents={contents}
- height={56}
+ height={Number(MAX_ROW_HEIGHT)}
GetValue={() => {
let field = props.Document.Get(props.fieldKey);
if (field && field instanceof Field) {
@@ -165,9 +158,9 @@ export class CollectionSchemaView extends CollectionSubView {
};
}
- @computed
- get columns() {
- return this.props.Document.GetList(KeyStore.ColumnsKey, [] as Key[]);
+ private createTarget = (ele: HTMLDivElement) => {
+ this._mainCont = ele;
+ super.CreateDropTarget(ele);
}
@action
@@ -187,31 +180,12 @@ export class CollectionSchemaView extends CollectionSubView {
//toggles preview side-panel of schema
@action
toggleExpander = (event: React.ChangeEvent<HTMLInputElement>) => {
- this._startSplitPercent = this.splitPercentage;
- if (this._startSplitPercent === this.splitPercentage) {
- this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0);
- }
- }
-
- @computed
- get findAllDocumentKeys(): { [id: string]: boolean } {
- const docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
- let keys: { [id: string]: boolean } = {};
- if (this._optionsActivated > -1) {
- // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields.
- // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be
- // invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked.
- // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu
- // is displayed (unlikely) it won't show up until something else changes.
- untracked(() => docs.map(doc => doc.GetAllPrototypes().map(proto => proto._proxies.forEach((val: any, key: string) => keys[key] = false))));
- }
- this.columns.forEach(key => keys[key.Id] = true);
- return keys;
+ this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0);
}
@action
onDividerMove = (e: PointerEvent): void => {
- let nativeWidth = this._mainCont.current!.getBoundingClientRect();
+ let nativeWidth = this._mainCont!.getBoundingClientRect();
this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100)));
}
@action
@@ -230,28 +204,6 @@ export class CollectionSchemaView extends CollectionSubView {
document.addEventListener('pointerup', this.onDividerUp);
}
- @observable _tableWidth = 0;
- @action
- setTableDimensions = (r: any) => {
- this._tableWidth = r.entry.width;
- }
- @action
- setScaling = (r: any) => {
- const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
- const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
- this._panelWidth = r.entry.width;
- this._panelHeight = r.entry.height ? r.entry.height : this._panelHeight;
- this._contentScaling = r.entry.width / selected!.GetNumber(KeyStore.NativeWidth, r.entry.width);
- }
-
- @computed
- get borderWidth() { return COLLECTION_BORDER_WIDTH; }
- getContentScaling = (): number => this._contentScaling;
- getPanelWidth = (): number => this._panelWidth;
- getPanelHeight = (): number => this._panelHeight;
- getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH - this._dividerX, - this.borderWidth).scale(1 / this._contentScaling);
- getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - this.borderWidth).scale(1 / this._contentScaling);
-
onPointerDown = (e: React.PointerEvent): void => {
if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) {
if (this.props.isSelected())
@@ -260,127 +212,141 @@ export class CollectionSchemaView extends CollectionSubView {
}
}
- @action
- addColumn = () => {
- this.columns.push(new Key(this.newKeyName));
- this.newKeyName = "";
- }
-
- @observable
- newKeyName: string = "";
-
- @action
- newKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- this.newKeyName = e.currentTarget.value;
- }
onWheel = (e: React.WheelEvent): void => {
if (this.props.active()) {
e.stopPropagation();
}
}
- @observable _optionsActivated: number = 0;
@action
- OptionsMenuDown = (e: React.PointerEvent) => {
- this._optionsActivated++;
+ addColumn = () => {
+ this.columns.push(new Key(this._newKeyName));
+ this._newKeyName = "";
}
- @observable previewScript: string = "this";
+ @action
+ newKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this._newKeyName = e.currentTarget.value;
+ }
+
+ @observable previewScript: string = "";
@action
onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.previewScript = e.currentTarget.value;
}
- render() {
- library.add(faCog);
- library.add(faPlus);
- const columns = this.columns;
+ get previewDocument(): Document | undefined {
const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
- //all the keys/columns that will be displayed in the schema
- const allKeys = this.findAllDocumentKeys;
- let doc: any = selected ? selected.Get(new Key(this.previewScript)) : undefined;
+ return selected ? (this.previewScript ? selected.Get(new Key(this.previewScript)) as Document : selected) : undefined;
+ }
+ get tableWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * (1 - this.splitPercentage / 100); }
+ get previewRegionHeight() { return this.props.PanelHeight() - 2 * this.borderWidth; }
+ get previewRegionWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * this.splitPercentage / 100; }
+
+ private previewDocNativeWidth = () => this.previewDocument!.GetNumber(KeyStore.NativeWidth, this.previewRegionWidth);
+ private previewDocNativeHeight = () => this.previewDocument!.GetNumber(KeyStore.NativeHeight, this.previewRegionHeight);
+ private previewContentScaling = () => {
+ let wscale = this.previewRegionWidth / (this.previewDocNativeWidth() ? this.previewDocNativeWidth() : this.previewRegionWidth);
+ if (wscale * this.previewDocNativeHeight() > this.previewRegionHeight)
+ return this.previewRegionHeight / (this.previewDocNativeHeight() ? this.previewDocNativeHeight() : this.previewRegionHeight);
+ return wscale;
+ }
+ private previewPanelWidth = () => this.previewDocNativeWidth() * this.previewContentScaling();
+ private previewPanelHeight = () => this.previewDocNativeHeight() * this.previewContentScaling();
+ get previewPanelCenteringOffset() { return (this.previewRegionWidth - this.previewDocNativeWidth() * this.previewContentScaling()) / 2; }
+ getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(
+ - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth - this.previewPanelCenteringOffset,
+ - this.borderWidth).scale(1 / this.previewContentScaling());
+ @computed
+ get previewPanel() {
// let doc = CompileScript(this.previewScript, { this: selected }, true)();
- let content = this._selectedIndex === -1 || !selected ? (null) : (
- <Measure onResize={this.setScaling}>
- {({ measureRef }) =>
- <div className="collectionSchemaView-content" ref={measureRef}>
- {doc instanceof Document ?
- <DocumentView Document={doc}
- addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
- isTopMost={false}
- selectOnLoad={false}
- ScreenToLocalTransform={this.getPreviewTransform}
- ContentScaling={this.getContentScaling}
- PanelWidth={this.getPanelWidth}
- PanelHeight={this.getPanelHeight}
- ContainingCollectionView={this.props.CollectionView}
- focus={emptyDocFunction}
- parentActive={this.props.active}
- onActiveChanged={this.props.onActiveChanged} /> : null}
- <input value={this.previewScript} onChange={this.onPreviewScriptChange}
- style={{ position: 'absolute', bottom: '0px' }} />
- </div>
- }
- </Measure>
- );
- let dividerDragger = this.splitPercentage === 0 ? (null) :
- <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />;
-
- //options button and menu
- let optionsMenu = !this.props.active() ? (null) : (<Flyout
- anchorPoint={anchorPoints.LEFT_TOP}
- content={<div>
- <div id="schema-options-header"><h5><b>Options</b></h5></div>
- <div id="options-flyout-div">
- <h6 className="schema-options-subHeader">Preview Window</h6>
- <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.splitPercentage !== 0} onChange={this.toggleExpander} /> Show Preview </div>
- <h6 className="schema-options-subHeader" >Displayed Columns</h6>
- <ul id="schema-col-checklist" >
- {Array.from(Object.keys(allKeys)).map(item =>
- (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />))}
- </ul>
- <input value={this.newKeyName} onChange={this.newKeyChange} />
- <button onClick={this.addColumn}><FontAwesomeIcon style={{ color: "white" }} icon="plus" size="lg" /></button>
+ return !this.previewDocument ? (null) : (
+ <div className="collectionSchemaView-previewRegion" style={{ width: `${this.previewRegionWidth}px` }}>
+ <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
+ <DocumentView Document={this.previewDocument} isTopMost={false} selectOnLoad={false}
+ addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
+ ScreenToLocalTransform={this.getPreviewTransform}
+ ContentScaling={this.previewContentScaling}
+ PanelWidth={this.previewPanelWidth} PanelHeight={this.previewPanelHeight}
+ ContainingCollectionView={this.props.CollectionView}
+ focus={emptyDocFunction}
+ parentActive={this.props.active}
+ whenActiveChanged={this.props.whenActiveChanged}
+ />
</div>
+ <input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange}
+ style={{ left: `calc(50% - ${Math.min(75, this.previewPanelWidth() / 2)}px)` }} />
</div>
- }>
- <button id="schemaOptionsMenuBtn" onPointerDown={this.OptionsMenuDown}><FontAwesomeIcon style={{ color: "white" }} icon="cog" size="sm" /></button>
- </Flyout>);
+ );
+ }
- return (
- <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel} ref={this._mainCont}>
- <div className="collectionSchemaView-dropTarget" onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}>
- <Measure onResize={this.setTableDimensions}>
- {({ measureRef }) =>
- <div className="collectionSchemaView-tableContainer" ref={measureRef} style={{ width: `calc(100% - ${this.splitPercentage}%)` }}>
- <ReactTable
- data={children}
- pageSize={children.length}
- page={0}
- showPagination={false}
- columns={columns.map(col => ({
- Header: col.Name,
- accessor: (doc: Document) => [doc, col],
- id: col.Id
- }))}
- column={{
- ...ReactTableDefaults.column,
- Cell: this.renderCell,
+ get documentKeysCheckList() {
+ const docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
+ let keys: { [id: string]: boolean } = {};
+ // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields.
+ // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be
+ // invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked.
+ // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu
+ // is displayed (unlikely) it won't show up until something else changes.
+ untracked(() => docs.map(doc => doc.GetAllPrototypes().map(proto => proto._proxies.forEach((val: any, key: string) => keys[key] = false))));
- }}
- getTrProps={this.getTrProps}
- />
- </div>}
- </Measure>
- {dividerDragger}
- <div className="collectionSchemaView-previewRegion" style={{ width: `calc(${this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0)}% - ${this.DIVIDER_WIDTH}px)` }}>
- {content}
+ this.columns.forEach(key => keys[key.Id] = true);
+ return Array.from(Object.keys(keys)).map(item =>
+ (<KeyToggle checked={keys[item]} key={item} keyId={item} toggle={this.toggleKey} />));
+ }
+
+ get tableOptionsPanel() {
+ return !this.props.active() ? (null) :
+ (<Flyout
+ anchorPoint={anchorPoints.LEFT_TOP}
+ content={<div>
+ <div id="schema-options-header"><h5><b>Options</b></h5></div>
+ <div id="options-flyout-div">
+ <h6 className="schema-options-subHeader">Preview Window</h6>
+ <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.splitPercentage !== 0} onChange={this.toggleExpander} /> Show Preview </div>
+ <h6 className="schema-options-subHeader" >Displayed Columns</h6>
+ <ul id="schema-col-checklist" >
+ {this.documentKeysCheckList}
+ </ul>
+ <input value={this._newKeyName} onChange={this.newKeyChange} />
+ <button onClick={this.addColumn}><FontAwesomeIcon style={{ color: "white" }} icon="plus" size="lg" /></button>
</div>
- {optionsMenu}
</div>
- </div >
+ }>
+ <button id="schemaOptionsMenuBtn" ><FontAwesomeIcon style={{ color: "white" }} icon="cog" size="sm" /></button>
+ </Flyout>);
+ }
+
+ @computed
+ get dividerDragger() {
+ return this.splitPercentage === 0 ? (null) :
+ <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />;
+ }
+
+ render() {
+ library.add(faCog);
+ library.add(faPlus);
+ const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
+ return (
+ <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel}
+ onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}>
+ <div className="collectionSchemaView-tableContainer" style={{ width: `${this.tableWidth}px` }}>
+ <ReactTable data={children} page={0} pageSize={children.length} showPagination={false}
+ columns={this.columns.map(col => ({
+ Header: col.Name,
+ accessor: (doc: Document) => [doc, col],
+ id: col.Id
+ }))}
+ column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }}
+ getTrProps={this.getTrProps}
+ />
+ </div>
+ {this.dividerDragger}
+ {this.previewPanel}
+ {this.tableOptionsPanel}
+ </div>
);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d3d69b1af..5c3b2e586 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -15,7 +15,6 @@ import { ServerUtils } from "../../../server/ServerUtil";
import { Server } from "../../Server";
import { FieldViewProps } from "../nodes/FieldView";
import * as rp from 'request-promise';
-import { emptyFunction } from "../../../Utils";
import { CollectionView } from "./CollectionView";
import { CollectionPDFView } from "./CollectionPDFView";
import { CollectionVideoView } from "./CollectionVideoView";
@@ -24,6 +23,8 @@ export interface CollectionViewProps extends FieldViewProps {
addDocument: (document: Document, allowDuplicates?: boolean) => boolean;
removeDocument: (document: Document) => boolean;
moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
+ PanelWidth: () => number;
+ PanelHeight: () => number;
}
export interface SubCollectionViewProps extends CollectionViewProps {
@@ -42,6 +43,9 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
}
}
+ protected CreateDropTarget(ele: HTMLDivElement) {
+ this.createDropTarget(ele);
+ }
@action
protected setCursorPosition(position: [number, number]) {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 51a02fc25..e0387f4b4 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,20 +1,17 @@
import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
import { faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable, trace } from "mobx";
+import { action, observable } from "mobx";
import { observer } from "mobx-react";
import { Document } from "../../../fields/Document";
import { FieldWaiting } from "../../../fields/Field";
import { KeyStore } from "../../../fields/KeyStore";
import { ListField } from "../../../fields/ListField";
-import { SetupDrag, DragManager } from "../../util/DragManager";
+import { DragManager, SetupDrag } from "../../util/DragManager";
import { EditableView } from "../EditableView";
-import "./CollectionTreeView.scss";
-import { CollectionView } from "./CollectionView";
-import * as globalCssVariables from "../../views/globalCssVariables.scss";
import { CollectionSubView } from "./CollectionSubView";
+import "./CollectionTreeView.scss";
import React = require("react");
-import { props } from 'bluebird';
export interface TreeViewProps {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 8868f7df0..20c5a84bf 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -23,10 +23,10 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
let l = this.props.LinkDocs;
let a = this.props.A;
let b = this.props.B;
- let x1 = a.GetNumber(KeyStore.X, 0) + (a.GetBoolean(KeyStore.Minimized, false) ? 5 : a.Width() / 2);
- let y1 = a.GetNumber(KeyStore.Y, 0) + (a.GetBoolean(KeyStore.Minimized, false) ? 5 : a.Height() / 2);
- let x2 = b.GetNumber(KeyStore.X, 0) + (b.GetBoolean(KeyStore.Minimized, false) ? 5 : b.Width() / 2);
- let y2 = b.GetNumber(KeyStore.Y, 0) + (b.GetBoolean(KeyStore.Minimized, false) ? 5 : b.Height() / 2);
+ let x1 = a.GetNumber(KeyStore.X, 0) + (a.GetBoolean(KeyStore.IsMinimized, false) ? 5 : a.Width() / 2);
+ let y1 = a.GetNumber(KeyStore.Y, 0) + (a.GetBoolean(KeyStore.IsMinimized, false) ? 5 : a.Height() / 2);
+ let x2 = b.GetNumber(KeyStore.X, 0) + (b.GetBoolean(KeyStore.IsMinimized, false) ? 5 : b.Width() / 2);
+ let y2 = b.GetNumber(KeyStore.Y, 0) + (b.GetBoolean(KeyStore.IsMinimized, false) ? 5 : b.Height() / 2);
return (
<line key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkLine" onPointerDown={this.onPointerDown}
style={{ strokeWidth: `${l.length * 5}` }}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 392bd514f..57706b51e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -1,11 +1,4 @@
@import "../../globalCssVariables";
-.collectionfreeformview-measure {
- position: inherit;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- }
.collectionfreeformview {
position: inherit;
top: 0;
@@ -52,7 +45,7 @@
}
opacity: 0.99;
- border-width: 0;
+ border-width: 0;
border-color: transparent;
border-style: solid;
border-radius: $border-radius;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 97708ce19..945c01059 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,6 +1,5 @@
import { action, computed, observable, trace } from "mobx";
import { observer } from "mobx-react";
-import Measure from "react-measure";
import { Document } from "../../../../fields/Document";
import { KeyStore } from "../../../../fields/KeyStore";
import { emptyFunction, returnFalse, returnOne } from "../../../../Utils";
@@ -22,14 +21,15 @@ import "./CollectionFreeFormView.scss";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
import v5 = require("uuid/v5");
+import { BooleanField } from "../../../../fields/BooleanField";
@observer
export class CollectionFreeFormView extends CollectionSubView {
private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type)
private _lastX: number = 0;
private _lastY: number = 0;
- @observable private _pwidth: number = 0;
- @observable private _pheight: number = 0;
+ private get _pwidth() { return this.props.PanelWidth(); }
+ private get _pheight() { return this.props.PanelHeight(); }
@computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
@computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
@@ -49,9 +49,7 @@ export class CollectionFreeFormView extends CollectionSubView {
this.addDocument(newBox, false);
}
private addDocument = (newBox: Document, allowDuplicates: boolean) => {
- if (this.isAnnotationOverlay) {
- newBox.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1));
- }
+ newBox.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1));
return this.props.addDocument(this.bringToFront(newBox), false);
}
private selectDocuments = (docs: Document[]) => {
@@ -76,16 +74,21 @@ export class CollectionFreeFormView extends CollectionSubView {
if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) {
const [x, y] = this.getTransform().transformPoint(de.x - de.data.xOffset, de.y - de.data.yOffset);
if (de.data.droppedDocuments.length) {
- let dropX = de.data.droppedDocuments[0].GetNumber(KeyStore.X, 0);
- let dropY = de.data.droppedDocuments[0].GetNumber(KeyStore.Y, 0);
+ let dragDoc = de.data.droppedDocuments[0];
+ let dropX = dragDoc.GetNumber(KeyStore.X, 0);
+ let dropY = dragDoc.GetNumber(KeyStore.Y, 0);
de.data.droppedDocuments.map(d => {
- d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0) - dropX));
- d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0) - dropY));
- if (!d.GetNumber(KeyStore.Width, 0)) {
- d.SetNumber(KeyStore.Width, 300);
- }
- if (!d.GetNumber(KeyStore.Height, 0)) {
- d.SetNumber(KeyStore.Height, 300);
+ d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0)) - dropX);
+ d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0)) - dropY);
+ if (!d.GetBoolean(KeyStore.IsMinimized, false)) {
+ if (!d.GetNumber(KeyStore.Width, 0)) {
+ d.SetNumber(KeyStore.Width, 300);
+ }
+ if (!d.GetNumber(KeyStore.Height, 0)) {
+ let nw = d.GetNumber(KeyStore.NativeWidth, 0);
+ let nh = d.GetNumber(KeyStore.NativeHeight, 0);
+ d.SetNumber(KeyStore.Height, nw && nh ? nh / nw * d.Width() : 300);
+ }
}
this.bringToFront(d);
});
@@ -185,14 +188,16 @@ export class CollectionFreeFormView extends CollectionSubView {
// if (modes[e.deltaMode] === 'pixels') coefficient = 50;
// else if (modes[e.deltaMode] === 'lines') coefficient = 1000; // This should correspond to line-height??
let deltaScale = (1 - (e.deltaY / coefficient));
+ if (deltaScale < 0) deltaScale = -deltaScale;
if (deltaScale * this.zoomScaling() < 1 && this.isAnnotationOverlay) {
deltaScale = 1 / this.zoomScaling();
}
let [x, y] = this.getTransform().transformPoint(e.clientX, e.clientY);
let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
- this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale);
- this.setPan(-localTransform.TranslateX / localTransform.Scale, -localTransform.TranslateY / localTransform.Scale);
+ let safeScale = Math.abs(localTransform.Scale);
+ this.props.Document.SetNumber(KeyStore.Scale, Math.abs(safeScale));
+ this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
e.stopPropagation();
}
}
@@ -248,7 +253,7 @@ export class CollectionFreeFormView extends CollectionSubView {
ContainingCollectionView: this.props.CollectionView,
focus: this.focusDocument,
parentActive: this.props.active,
- onActiveChanged: this.props.active,
+ whenActiveChanged: this.props.active,
};
}
@@ -258,7 +263,9 @@ export class CollectionFreeFormView extends CollectionSubView {
let docviews = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((prev, doc) => {
var page = doc.GetNumber(KeyStore.Page, -1);
if (page === curPage || page === -1) {
- prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
+ let minim = doc.GetT(KeyStore.IsMinimized, BooleanField);
+ if (minim === undefined || (minim && !minim.Data))
+ prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
}
return prev;
}, [] as JSX.Element[]);
@@ -269,11 +276,6 @@ export class CollectionFreeFormView extends CollectionSubView {
}
@action
- onResize = (r: any) => {
- this._pwidth = r.entry.width;
- this._pheight = r.entry.height;
- }
- @action
onCursorMove = (e: React.PointerEvent) => {
super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
}
@@ -281,29 +283,24 @@ export class CollectionFreeFormView extends CollectionSubView {
render() {
const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
return (
- <Measure onResize={this.onResize}>
- {({ measureRef }) => (
- <div className="collectionfreeformview-measure" ref={measureRef}>
- <div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
- onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} >
- <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
- addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox}
- getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
- <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
- zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
- <CollectionFreeFormBackgroundView {...this.getDocumentViewProps(this.props.Document)} />
- <CollectionFreeFormLinksView {...this.props} key="freeformLinks">
- <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
- {this.childViews}
- </InkingCanvas>
- </CollectionFreeFormLinksView>
- <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
- </CollectionFreeFormViewPannableContents>
- <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} />
- </MarqueeView>
- </div>
- </div>)}
- </Measure>
+ <div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
+ onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} >
+ <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
+ addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox}
+ getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
+ <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
+ zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
+ <CollectionFreeFormBackgroundView {...this.getDocumentViewProps(this.props.Document)} />
+ <CollectionFreeFormLinksView {...this.props} key="freeformLinks">
+ <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
+ {this.childViews}
+ </InkingCanvas>
+ </CollectionFreeFormLinksView>
+ <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
+ </CollectionFreeFormViewPannableContents>
+ <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} />
+ </MarqueeView>
+ </div>
);
}
}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 65f461b27..bf918beba 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -11,6 +11,7 @@ import { undoBatch } from "../../../util/UndoManager";
import { InkingCanvas } from "../../InkingCanvas";
import { PreviewCursor } from "../../PreviewCursor";
import { CollectionFreeFormView } from "./CollectionFreeFormView";
+import { MINIMIZED_ICON_SIZE } from '../../../views/globalCssVariables.scss'
import "./MarqueeView.scss";
import React = require("react");
diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss
index 5c8e9c8fc..4f68b71b0 100644
--- a/src/client/views/globalCssVariables.scss
+++ b/src/client/views/globalCssVariables.scss
@@ -23,7 +23,11 @@ $mainTextInput-zindex: 999; // then text input overlay so that it's context menu
$docDecorations-zindex: 998; // then doc decorations appear over everything else
$remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right?
$COLLECTION_BORDER_WIDTH: 1;
+$MINIMIZED_ICON_SIZE:25;
+$MAX_ROW_HEIGHT: 44px;
:export {
contextMenuZindex: $contextMenu-zindex;
COLLECTION_BORDER_WIDTH: $COLLECTION_BORDER_WIDTH;
+ MINIMIZED_ICON_SIZE: $MINIMIZED_ICON_SIZE;
+ MAX_ROW_HEIGHT: $MAX_ROW_HEIGHT;
} \ No newline at end of file
diff --git a/src/client/views/globalCssVariables.scss.d.ts b/src/client/views/globalCssVariables.scss.d.ts
index e874b815d..9788d31f7 100644
--- a/src/client/views/globalCssVariables.scss.d.ts
+++ b/src/client/views/globalCssVariables.scss.d.ts
@@ -1,7 +1,9 @@
interface IGlobalScss {
contextMenuZindex: string; // context menu shows up over everything
- COLLECTION_BORDER_WIDTH: number;
+ COLLECTION_BORDER_WIDTH: string;
+ MINIMIZED_ICON_SIZE: string;
+ MAX_ROW_HEIGHT: string;
}
declare const globalCssVariables: IGlobalScss;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 8cf7a0dd2..1d42b3899 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -17,7 +17,7 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
@computed
get transform(): string {
- return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.props.Document.GetNumber(KeyStore.X, 0)}px, ${this.props.Document.GetNumber(KeyStore.Y, 0)}px) scale(${this.zoom}, ${this.zoom}) `;
+ return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}, ${this.zoom}) `;
}
@computed get zoom(): number { return 1 / this.props.Document.GetNumber(KeyStore.Zoom, 1); }
@@ -45,14 +45,20 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
this.props.Document.SetData(KeyStore.ZIndex, h, NumberField);
}
+ get X() {
+ return this.props.Document.GetNumber(KeyStore.X, 0);
+ }
+ get Y() {
+ return this.props.Document.GetNumber(KeyStore.Y, 0);
+ }
getTransform = (): Transform =>
this.props.ScreenToLocalTransform()
- .translate(-this.props.Document.GetNumber(KeyStore.X, 0), -this.props.Document.GetNumber(KeyStore.Y, 0))
+ .translate(-this.X, -this.Y)
.scale(1 / this.contentScaling()).scale(1 / this.zoom)
contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1;
- panelWidth = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelWidth();
- panelHeight = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelHeight();
+ panelWidth = () => this.props.PanelWidth();
+ panelHeight = () => this.props.PanelHeight();
@computed
get docView() {
@@ -66,22 +72,22 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
render() {
let zoomFade = 1;
- // //var zoom = doc.GetNumber(KeyStore.Zoom, 1);
- // let transform = (this.props.ScreenToLocalTransform().scale(this.props.ContentScaling())).inverse();
+ //var zoom = doc.GetNumber(KeyStore.Zoom, 1);
+ // let transform = this.getTransform().scale(this.contentScaling()).inverse();
// var [sptX, sptY] = transform.transformPoint(0, 0);
// let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
// let w = bptX - sptX;
// //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
// let fadeUp = .75 * 1800;
// let fadeDown = .075 * 1800;
- // zoomFade = w < fadeDown || w > fadeUp ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
+ // zoomFade = w < fadeDown /* || w > fadeUp */ ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
return (
<div className="collectionFreeFormDocumentView-container" ref={this._mainCont} style={{
opacity: zoomFade,
transformOrigin: "left top",
transform: this.transform,
- pointerEvents: "all",
+ pointerEvents: (zoomFade < 0.09 ? "none" : "all"),
width: this.width,
height: this.height,
position: "absolute",
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 76f852601..07599c345 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -15,6 +15,7 @@ import { DocumentViewProps } from "./DocumentView";
import "./DocumentView.scss";
import { FormattedTextBox } from "./FormattedTextBox";
import { ImageBox } from "./ImageBox";
+import { IconBox } from "./IconBox";
import { KeyValueBox } from "./KeyValueBox";
import { PDFBox } from "./PDFBox";
import { VideoBox } from "./VideoBox";
@@ -62,7 +63,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
return <p>Error loading layout keys</p>;
}
return <JsxParser
- components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
+ components={{ FormattedTextBox, ImageBox, IconBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
bindings={this.CreateBindings()}
jsx={this.layout}
showWarnings={true}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 690ee50e8..7c72fb6e6 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -4,7 +4,7 @@
position: inherit;
top: 0;
left:0;
- background: $light-color; //overflow: hidden;
+ // background: $light-color; //overflow: hidden;
transform-origin: left top;
&.minimized {
@@ -13,7 +13,6 @@
}
.top {
- background: #232323;
height: 20px;
cursor: pointer;
}
@@ -31,18 +30,4 @@
}
.documentView-node-topmost {
background: white;
-}
-
-.minimized-box {
- height: 10px;
- width: 10px;
- border-radius: 2px;
- background: $dark-color;
- transform-origin: left top;
-}
-
-.minimized-box:hover {
- background: $main-accent;
- transform: scale(1.15);
- cursor: pointer;
} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4f7909692..c06fe4c8d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -36,12 +36,11 @@ export interface DocumentViewProps {
focus: (doc: Document) => void;
selectOnLoad: boolean;
parentActive: () => boolean;
- onActiveChanged: (isActive: boolean) => void;
+ whenActiveChanged: (isActive: boolean) => void;
}
const schema = createSchema({
layout: "string",
- minimized: "boolean",
nativeWidth: "number",
nativeHeight: "number",
backgroundColor: "string"
@@ -136,7 +135,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
- if (!this.topMost || e.buttons === 2 || e.altKey) {
+ if (!e.altKey && (!this.topMost || e.buttons === 2)) {
this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey);
}
}
@@ -147,10 +146,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
e.stopPropagation();
- if (!SelectionManager.IsSelected(this) && e.button !== 2 &&
- Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) {
- SelectionManager.SelectDoc(this, e.ctrlKey);
- }
+ if (!SelectionManager.IsSelected(this) && e.button !== 2)
+ if (Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) {
+ if (this.props.Document.Get(KeyStore.MaximizedDoc) instanceof Document) {
+ this.props.Document.GetAsync(KeyStore.MaximizedDoc, maxdoc => {
+ if (maxdoc instanceof Document) {
+ this.props.addDocument && this.props.addDocument(maxdoc, false);
+ this.toggleMinimize(maxdoc, this.props.Document);
+ }
+ });
+ } else {
+ SelectionManager.SelectDoc(this, e.ctrlKey);
+ }
+ }
}
stopPropagation = (e: React.SyntheticEvent) => {
e.stopPropagation();
@@ -179,10 +187,84 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
}
+ @action createIcon = (layoutString: string): Document => {
+ let iconDoc = Documents.IconDocument(layoutString);
+ iconDoc.SetBoolean(KeyStore.IsMinimized, false);
+ iconDoc.SetNumber(KeyStore.NativeWidth, 0);
+ iconDoc.SetNumber(KeyStore.NativeHeight, 0);
+ iconDoc.Set(KeyStore.Prototype, this.props.Document);
+ iconDoc.Set(KeyStore.MaximizedDoc, this.props.Document);
+ this.props.Document.Set(KeyStore.MinimizedDoc, iconDoc);
+ this.props.addDocument && this.props.addDocument(iconDoc, false);
+ return iconDoc;
+ }
+
+ animateTransition(icon: number[], targ: number[], width: number, height: number, stime: number, target: Document, maximizing: boolean) {
+ setTimeout(() => {
+ let now = Date.now();
+ let progress = Math.min(1, (now - stime) / 200);
+ let pval = maximizing ?
+ [icon[0] + (targ[0] - icon[0]) * progress, icon[1] + (targ[1] - icon[1]) * progress] :
+ [targ[0] + (icon[0] - targ[0]) * progress, targ[1] + (icon[1] - targ[1]) * progress];
+ target.SetNumber(KeyStore.Width, maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress);
+ target.SetNumber(KeyStore.Height, maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress);
+ target.SetNumber(KeyStore.X, pval[0]);
+ target.SetNumber(KeyStore.Y, pval[1]);
+ if (now < stime + 200) {
+ this.animateTransition(icon, targ, width, height, stime, target, maximizing);
+ }
+ else {
+ if (!maximizing) {
+ target.SetBoolean(KeyStore.IsMinimized, true);
+ target.SetNumber(KeyStore.X, targ[0]);
+ target.SetNumber(KeyStore.Y, targ[1]);
+ target.SetNumber(KeyStore.Width, width);
+ target.SetNumber(KeyStore.Height, height);
+ }
+ this._completed = true;
+ }
+ },
+ 2);
+ }
+
+ _completed = true;
+
@action
- public minimize = (): void => {
- this.props.Document.SetBoolean(KeyStore.Minimized, true);
+ public toggleMinimize = (maximized: Document, minim: Document): void => {
SelectionManager.DeselectAll();
+ if (maximized instanceof Document && this._completed) {
+ this._completed = false;
+ let minimized = maximized.GetBoolean(KeyStore.IsMinimized, false);
+ maximized.SetBoolean(KeyStore.IsMinimized, false);
+ this.animateTransition(
+ [minim.GetNumber(KeyStore.X, 0), minim.GetNumber(KeyStore.Y, 0)],
+ [maximized.GetNumber(KeyStore.X, 0), maximized.GetNumber(KeyStore.Y, 0)],
+ maximized.GetNumber(KeyStore.Width, 0), maximized.GetNumber(KeyStore.Height, 0),
+ Date.now(), maximized, minimized);
+ }
+ }
+
+ @action
+ public minimize = (): void => {
+ this.props.Document.GetAsync(KeyStore.MinimizedDoc, mindoc => {
+ if (mindoc === undefined) {
+ this.props.Document.GetAsync(KeyStore.BackgroundLayout, field => {
+ if (field instanceof TextField) {
+ this.toggleMinimize(this.props.Document, this.createIcon(field.Data));
+ }
+ else this.props.Document.GetAsync(KeyStore.Layout, field => {
+ if (field instanceof TextField) {
+ this.createIcon(field.Data);
+ this.toggleMinimize(this.props.Document, this.createIcon(field.Data));
+ }
+ });
+ });
+ }
+ else if (mindoc instanceof Document) {
+ this.props.addDocument && this.props.addDocument(mindoc, false);
+ this.toggleMinimize(this.props.Document, mindoc);
+ }
+ });
}
@undoBatch
@@ -250,7 +332,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
e.preventDefault();
- !this.isMinimized() && ContextMenu.Instance.addItem({ description: "Minimize", event: this.minimize });
ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked });
ContextMenu.Instance.addItem({ description: "Fields", event: this.fieldsClicked });
ContextMenu.Instance.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) });
@@ -265,9 +346,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
- @action
- expand = () => this.Document.minimized = false
- isMinimized = () => FieldValue(this.Document.minimized) || false;
isSelected = () => SelectionManager.IsSelected(this);
select = (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed);
@@ -275,15 +353,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@computed get nativeHeight() { return FieldValue(this.Document.nativeHeight) || 0; }
@computed get contents() { return (<DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} layoutKey={KeyStore.Layout} />); }
+
render() {
var scaling = this.props.ContentScaling();
var nativeHeight = this.nativeHeight > 0 ? this.nativeHeight.toString() + "px" : "100%";
var nativeWidth = this.nativeWidth > 0 ? this.nativeWidth.toString() + "px" : "100%";
- if (this.isMinimized()) {
- return <div className="minimized-box" ref={this._mainCont} onClick={this.expand} onDrop={this.onDrop}
- style={{ transform: `scale(${scaling} , ${scaling})` }} onPointerDown={this.onPointerDown} />;
- }
return (
<div className={`documentView-node${this.props.isTopMost ? "-topmost" : ""}`}
ref={this._mainCont}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index ebd25f937..93e385821 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -23,6 +23,8 @@ import { returnFalse, emptyDocFunction, emptyFunction, returnOne } from "../../.
import { CollectionView } from "../collections/CollectionView";
import { CollectionPDFView } from "../collections/CollectionPDFView";
import { CollectionVideoView } from "../collections/CollectionVideoView";
+import { IconField } from "../../../fields/IconFIeld";
+import { IconBox } from "./IconBox";
//
@@ -43,7 +45,7 @@ export interface FieldViewProps {
moveDocument?: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
active: () => boolean;
- onActiveChanged: (isActive: boolean) => void;
+ whenActiveChanged: (isActive: boolean) => void;
focus: (doc: Document) => void;
}
@@ -72,6 +74,9 @@ export class FieldView extends React.Component<FieldViewProps> {
else if (field instanceof ImageField) {
return <ImageBox {...this.props} />;
}
+ else if (field instanceof IconField) {
+ return <IconBox {...this.props} />;
+ }
else if (field instanceof VideoField) {
return <VideoBox {...this.props} />;
}
@@ -95,7 +100,7 @@ export class FieldView extends React.Component<FieldViewProps> {
layoutKey={KeyStore.Layout}
ContainingCollectionView={this.props.ContainingCollectionView}
parentActive={this.props.active}
- onActiveChanged={this.props.onActiveChanged} />
+ whenActiveChanged={this.props.whenActiveChanged} />
);
}
else if (field instanceof ListField) {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index bff8ca7a4..639dae30a 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -19,6 +19,7 @@ import { MainOverlayTextBox } from "../MainOverlayTextBox";
import { FieldView, FieldViewProps } from "./FieldView";
import "./FormattedTextBox.scss";
import React = require("react");
+import { SelectionManager } from "../../util/SelectionManager";
const { buildMenuItems } = require("prosemirror-example-setup");
const { menuBar } = require("prosemirror-menu");
@@ -251,6 +252,9 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
}
onKeyPress(e: React.KeyboardEvent) {
+ if (e.key == "Escape") {
+ SelectionManager.DeselectAll();
+ }
e.stopPropagation();
if (e.keyCode === 9) e.preventDefault();
// stop propagation doesn't seem to stop propagation of native keyboard events.
diff --git a/src/client/views/nodes/IconBox.scss b/src/client/views/nodes/IconBox.scss
new file mode 100644
index 000000000..ce0ee2e09
--- /dev/null
+++ b/src/client/views/nodes/IconBox.scss
@@ -0,0 +1,12 @@
+
+@import "../globalCssVariables";
+.iconBox-container {
+ position: absolute;
+ left:0;
+ top:0;
+ svg {
+ width: 100% !important;
+ height: 100%;
+ background: white;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
new file mode 100644
index 000000000..9c90c0a0e
--- /dev/null
+++ b/src/client/views/nodes/IconBox.tsx
@@ -0,0 +1,45 @@
+import React = require("react");
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed } from "mobx";
+import { observer } from "mobx-react";
+import { Document } from '../../../fields/Document';
+import { IconField } from "../../../fields/IconFIeld";
+import { KeyStore } from "../../../fields/KeyStore";
+import { SelectionManager } from "../../util/SelectionManager";
+import { FieldView, FieldViewProps } from './FieldView';
+import "./IconBox.scss";
+
+
+library.add(faCaretUp);
+library.add(faObjectGroup);
+library.add(faStickyNote);
+library.add(faFilePdf);
+library.add(faFilm);
+
+@observer
+export class IconBox extends React.Component<FieldViewProps> {
+ public static LayoutString() { return FieldView.LayoutString(IconBox); }
+
+ @computed get maximized() { return this.props.Document.GetT(KeyStore.MaximizedDoc, Document); }
+ @computed get layout(): string { return this.props.Document.GetData(this.props.fieldKey, IconField, "<p>Error loading layout data</p>" as string); }
+ @computed get minimizedIcon() { return IconBox.DocumentIcon(this.layout); }
+
+ public static DocumentIcon(layout: string) {
+ let button = layout.indexOf("PDFBox") !== -1 ? faFilePdf :
+ layout.indexOf("ImageBox") !== -1 ? faImage :
+ layout.indexOf("Formatted") !== -1 ? faStickyNote :
+ layout.indexOf("Video") !== -1 ? faFilm :
+ layout.indexOf("Collection") !== -1 ? faObjectGroup :
+ faCaretUp;
+ return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />
+ }
+
+ render() {
+ return (
+ <div className="iconBox-container">
+ {this.minimizedIcon}
+ </div>);
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 71b431b84..fd5381b77 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -38,7 +38,10 @@ export class ImageBox extends React.Component<FieldViewProps> {
onLoad = (target: any) => {
var h = this._imgRef.current!.naturalHeight;
var w = this._imgRef.current!.naturalWidth;
- if (this._photoIndex === 0) this.props.Document.SetNumber(KeyStore.NativeHeight, this.props.Document.GetNumber(KeyStore.NativeWidth, 0) * h / w);
+ if (this._photoIndex === 0) {
+ this.props.Document.SetNumber(KeyStore.NativeHeight, this.props.Document.GetNumber(KeyStore.NativeWidth, 0) * h / w);
+ this.props.Document.SetNumber(KeyStore.Height, this.props.Document.Width() * h / w);
+ }
}
@@ -65,7 +68,7 @@ export class ImageBox extends React.Component<FieldViewProps> {
if (layout.indexOf(ImageBox.name) !== -1) {
let imgData = this.props.Document.Get(KeyStore.Data);
if (imgData instanceof ImageField && imgData) {
- this.props.Document.Set(KeyStore.Data, new ListField([imgData]));
+ this.props.Document.SetOnPrototype(KeyStore.Data, new ListField([imgData]));
}
let imgList = this.props.Document.GetList(KeyStore.Data, [] as any[]);
if (imgList) {
@@ -137,6 +140,7 @@ export class ImageBox extends React.Component<FieldViewProps> {
@action
onDotDown(index: number) {
this._photoIndex = index;
+ this.props.Document.SetNumber(KeyStore.CurPage, index);
}
dots(paths: string[]) {
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 5d69f23b2..d480eb5af 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -48,7 +48,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
isTopMost: false,
selectOnLoad: false,
active: returnFalse,
- onActiveChanged: emptyFunction,
+ whenActiveChanged: emptyFunction,
ScreenToLocalTransform: Transform.Identity,
focus: emptyDocFunction,
};
diff --git a/src/fields/IconFIeld.ts b/src/fields/IconFIeld.ts
new file mode 100644
index 000000000..a6694cc49
--- /dev/null
+++ b/src/fields/IconFIeld.ts
@@ -0,0 +1,25 @@
+import { BasicField } from "./BasicField";
+import { FieldId } from "./Field";
+import { Types } from "../server/Message";
+
+export class IconField extends BasicField<string> {
+ constructor(data: string = "", id?: FieldId, save: boolean = true) {
+ super(data, save, id);
+ }
+
+ ToScriptString(): string {
+ return `new IconField("${this.Data}")`;
+ }
+
+ Copy() {
+ return new IconField(this.Data);
+ }
+
+ ToJson() {
+ return {
+ type: Types.Icon,
+ data: this.Data,
+ id: this.Id
+ };
+ }
+} \ No newline at end of file
diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts
index 27b9635af..1607d4c15 100644
--- a/src/new_fields/Schema.ts
+++ b/src/new_fields/Schema.ts
@@ -1,8 +1,8 @@
import { Interface, ToInterface, Cast, FieldCtor, ToConstructor } from "./Types";
import { Doc } from "./Doc";
-export type makeInterface<T extends Interface> = Partial<ToInterface<T>> & Doc;
-export function makeInterface<T extends Interface>(schema: T): (doc: Doc) => makeInterface<T> {
+export type makeInterface<T extends Interface, U extends Doc = Doc> = Partial<ToInterface<T>> & U;
+export function makeInterface<T extends Interface, U extends Doc>(schema: T): (doc: U) => makeInterface<T, U> {
return function (doc: any) {
return new Proxy(doc, {
get(target, prop) {
diff --git a/src/server/Message.ts b/src/server/Message.ts
index 843a923d1..b01934724 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -14,7 +14,7 @@ export class Message<T> {
}
export enum Types {
- Number, List, Key, Image, Web, Document, Text, RichText, DocumentReference,
+ Number, List, Key, Image, Web, Document, Text, Icon, RichText, DocumentReference,
Html, Video, Audio, Ink, PDF, Tuple, HistogramOp, Boolean, Script,
}
diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts
index 818230c1a..79ca5e55d 100644
--- a/src/server/ServerUtil.ts
+++ b/src/server/ServerUtil.ts
@@ -18,6 +18,7 @@ import { NumberField } from "./../fields/NumberField";
import { RichTextField } from "./../fields/RichTextField";
import { TextField } from "./../fields/TextField";
import { Transferable, Types } from "./Message";
+import { IconField } from "../fields/IconFIeld";
export class ServerUtils {
public static prepend(extension: string): string {
@@ -37,6 +38,7 @@ export class ServerUtils {
case Types.Boolean: return new BooleanField(json.data, json.id, false);
case Types.Number: return new NumberField(json.data, json.id, false);
case Types.Text: return new TextField(json.data, json.id, false);
+ case Types.Icon: return new IconField(json.data, json.id, false);
case Types.Html: return new HtmlField(json.data, json.id, false);
case Types.Web: return new WebField(new URL(json.data), json.id, false);
case Types.RichText: return new RichTextField(json.data, json.id, false);