aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/Server.ts26
-rw-r--r--src/client/documents/Documents.ts39
-rw-r--r--src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx6
-rw-r--r--src/client/northstar/manager/Gateway.ts12
-rw-r--r--src/client/util/DragManager.ts11
-rw-r--r--src/client/util/RichTextSchema.tsx87
-rw-r--r--src/client/util/SelectionManager.ts2
-rw-r--r--src/client/util/TooltipTextMenu.scss240
-rw-r--r--src/client/util/TooltipTextMenu.tsx189
-rw-r--r--src/client/views/ContextMenu.scss2
-rw-r--r--src/client/views/DocumentDecorations.scss36
-rw-r--r--src/client/views/DocumentDecorations.tsx7
-rw-r--r--src/client/views/Main.scss25
-rw-r--r--src/client/views/Main.tsx77
-rw-r--r--src/client/views/_global_variables.scss11
-rw-r--r--src/client/views/_global_variables.scss.d.ts7
-rw-r--r--src/client/views/_global_variables.ts8
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx4
-rw-r--r--src/client/views/collections/CollectionPDFView.tsx3
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx6
-rw-r--r--src/client/views/collections/CollectionSubView.tsx58
-rw-r--r--src/client/views/collections/CollectionVideoView.tsx3
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss24
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx32
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx22
-rw-r--r--src/client/views/nodes/DocumentView.tsx36
-rw-r--r--src/client/views/nodes/FieldView.tsx10
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss1
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx77
-rw-r--r--src/client/views/nodes/VideoBox.tsx2
-rw-r--r--src/fields/KeyStore.ts1
-rw-r--r--tsconfig.json42
-rw-r--r--webpack.config.js180
34 files changed, 923 insertions, 364 deletions
diff --git a/src/client/Server.ts b/src/client/Server.ts
index 857101a33..3bbbebe72 100644
--- a/src/client/Server.ts
+++ b/src/client/Server.ts
@@ -1,10 +1,10 @@
import { Key } from "../fields/Key";
-import { ObservableMap, action, reaction } from "mobx";
+import { ObservableMap, action, reaction, runInAction } from "mobx";
import { Field, FieldWaiting, FIELD_WAITING, Opt, FieldId } from "../fields/Field";
import { Document } from "../fields/Document";
import { SocketStub, FieldMap } from "./SocketStub";
import * as OpenSocket from 'socket.io-client';
-import { Utils } from "./../Utils";
+import { Utils, emptyFunction } from "./../Utils";
import { MessageStore, Types } from "./../server/Message";
export class Server {
@@ -59,14 +59,14 @@ export class Server {
public static GetFields(fieldIds: FieldId[]): Promise<{ [id: string]: Field }>;
public static GetFields(fieldIds: FieldId[], callback: (fields: FieldMap) => any): void;
public static GetFields(fieldIds: FieldId[], callback?: (fields: FieldMap) => any): Promise<FieldMap> | void {
- let fn = (cb: (fields: FieldMap) => void) => {
+ let fn = action((cb: (fields: FieldMap) => void) => {
let neededFieldIds: FieldId[] = [];
let waitingFieldIds: FieldId[] = [];
- let existingFields: { [id: string]: Field } = {};
+ let existingFields: FieldMap = {};
for (let id of fieldIds) {
let field = this.ClientFieldsCached.get(id);
- if (!field) {
+ if (field === undefined) {
neededFieldIds.push(id);
this.ClientFieldsCached.set(id, FieldWaiting);
} else if (field === FieldWaiting) {
@@ -79,7 +79,7 @@ export class Server {
for (let id of neededFieldIds) {
let field = fields[id];
if (field) {
- if (!(this.ClientFieldsCached.get(field.Id) instanceof Field)) {
+ if (this.ClientFieldsCached.get(field.Id) === FieldWaiting) {
this.ClientFieldsCached.set(field.Id, field);
} else {
throw new Error("we shouldn't be trying to replace things that are already in the cache");
@@ -94,17 +94,17 @@ export class Server {
}
reaction(() => waitingFieldIds.map(id => this.ClientFieldsCached.get(id)),
(cachedFields, reaction) => {
- if (!cachedFields.some(field => !field)) {
+ if (!cachedFields.some(field => field === FieldWaiting)) {
+ const realFields = cachedFields as Opt<Field>[];
reaction.dispose();
- for (let field of cachedFields) {
- let realField = field as Field;
- existingFields[realField.Id] = realField;
- }
+ waitingFieldIds.forEach((id, index) => {
+ existingFields[id] = realFields[index];
+ });
cb({ ...fields, ...existingFields });
}
}, { fireImmediately: true });
}));
- };
+ });
if (callback) {
fn(callback);
} else {
@@ -167,7 +167,7 @@ export class Server {
if (f) {
// console.log("Applying : " + field._id);
f.UpdateFromServer(field.data);
- f.init(() => { });
+ f.init(emptyFunction);
} else {
// console.log("Not applying wa : " + field._id);
}
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 72e6e57ab..3c36fe500 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,6 +1,6 @@
import { AudioField } from "../../fields/AudioField";
import { Document } from "../../fields/Document";
-import { Field } from "../../fields/Field";
+import { Field, Opt } from "../../fields/Field";
import { HtmlField } from "../../fields/HtmlField";
import { ImageField } from "../../fields/ImageField";
import { InkField, StrokeData } from "../../fields/InkField";
@@ -26,6 +26,12 @@ import { KeyValueBox } from "../views/nodes/KeyValueBox";
import { PDFBox } from "../views/nodes/PDFBox";
import { VideoBox } from "../views/nodes/VideoBox";
import { WebBox } from "../views/nodes/WebBox";
+import { Gateway } from "../northstar/manager/Gateway";
+import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
+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";
export interface DocumentOptions {
x?: number;
@@ -200,6 +206,31 @@ export namespace Documents {
export function PdfDocument(url: string, options: DocumentOptions = {}) {
return assignToDelegate(SetInstanceOptions(pdfProto, options, [new URL(url), PDFField]).MakeDelegate(), options);
}
+ export async function DBDocument(url: string, options: DocumentOptions = {}) {
+ let schemaName = options.title ? options.title : "-no schema-";
+ let ctlog = await Gateway.Instance.GetSchema(url, schemaName);
+ if (ctlog && ctlog.schemas) {
+ let schema = ctlog.schemas[0];
+ let schemaDoc = Documents.TreeDocument([], { ...options, nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: schema.displayName! });
+ let schemaDocuments = schemaDoc.GetList(KeyStore.Data, [] as Document[]);
+ CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => {
+ Server.GetField(attr.displayName! + ".alias", action((field: Opt<Field>) => {
+ if (field instanceof Document) {
+ schemaDocuments.push(field);
+ } else {
+ var atmod = new ColumnAttributeModel(attr);
+ let histoOp = new HistogramOperation(schema.displayName!,
+ new AttributeTransformationModel(atmod, AggregateFunction.None),
+ new AttributeTransformationModel(atmod, AggregateFunction.Count),
+ new AttributeTransformationModel(atmod, AggregateFunction.Count));
+ schemaDocuments.push(Documents.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! }, undefined, attr.displayName! + ".alias"));
+ }
+ }));
+ });
+ return schemaDoc;
+ }
+ return Documents.TreeDocument([], { width: 50, height: 100, title: schemaName });
+ }
export function WebDocument(url: string, options: DocumentOptions = {}) {
return assignToDelegate(SetInstanceOptions(webProto, options, [new URL(url), WebField]).MakeDelegate(), options);
}
@@ -242,13 +273,15 @@ export namespace Documents {
<div style="position:relative; height:15%; text-align:center; ">`
+ FormattedTextBox.LayoutString("CaptionKey") +
`</div>
- </div>`; }
+ </div>`;
+ }
export function FixedCaption(fieldName: string = "Caption") {
return `<div style="position:absolute; height:30px; bottom:0; width:100%">
<div style="position:absolute; width:100%; height:100%; text-align:center;bottom:0;">`
+ FormattedTextBox.LayoutString(fieldName + "Key") +
`</div>
- </div>`; }
+ </div>`;
+ }
function OuterCaption() {
return (`
diff --git a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
index 4c5bdb14b..721bf6a89 100644
--- a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx
@@ -1,7 +1,7 @@
import React = require("react");
import { computed, observable, reaction, runInAction, trace, action } from "mobx";
import { observer } from "mobx-react";
-import { Utils as DashUtils } from '../../../Utils';
+import { Utils as DashUtils, emptyFunction } from '../../../Utils';
import { FilterModel } from "../../northstar/core/filter/FilterModel";
import { ModelHelpers } from "../../northstar/model/ModelHelpers";
import { ArrayUtil } from "../../northstar/utils/ArrayUtil";
@@ -49,7 +49,7 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
private getSelectionToggle(binPrimitives: HistogramBinPrimitive[], allBrushIndex: number, filterModel: FilterModel) {
let rawAllBrushPrim = ArrayUtil.FirstOrDefault(binPrimitives, bp => bp.BrushIndex === allBrushIndex);
if (!rawAllBrushPrim) {
- return () => { };
+ return emptyFunction;
}
let allBrushPrim = rawAllBrushPrim;
return () => runInAction(() => {
@@ -97,7 +97,7 @@ export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesP
let trans1Ypercent = `${yFrom / this.renderDimension * 100}%`;
return <line className="histogramboxprimitives-line" key={DashUtils.GenerateGuid()} x1={trans1Xpercent} x2={`${trans2Xpercent}`} y1={trans1Ypercent} y2={`${trans2Ypercent}`} />;
}
- drawRect(r: PIXIRectangle, barAxis: number, color: number | undefined, classExt: string, tapHandler: () => void = () => { }) {
+ drawRect(r: PIXIRectangle, barAxis: number, color: number | undefined, classExt: string, tapHandler: () => void = emptyFunction) {
if (r.height < 0) {
r.y += r.height;
r.height = -r.height;
diff --git a/src/client/northstar/manager/Gateway.ts b/src/client/northstar/manager/Gateway.ts
index 8f3b6b11c..207a9ad19 100644
--- a/src/client/northstar/manager/Gateway.ts
+++ b/src/client/northstar/manager/Gateway.ts
@@ -23,9 +23,9 @@ export class Gateway {
}
}
- public async GetSchema(dbName: string): Promise<Catalog> {
+ public async GetSchema(pathname: string, schemaname: string): Promise<Catalog> {
try {
- const json = await this.MakeGetRequest("schema", undefined, dbName);
+ const json = await this.MakeGetRequest("schema", undefined, { path: pathname, schema: schemaname });
const cat = Catalog.fromJS(json);
return cat;
}
@@ -144,13 +144,13 @@ export class Gateway {
});
}
- public async MakeGetRequest(endpoint: string, signal?: AbortSignal, data?: any): Promise<any> {
- let url = !data ? Gateway.ConstructUrl(endpoint) :
+ public async MakeGetRequest(endpoint: string, signal?: AbortSignal, params?: any): Promise<any> {
+ let url = !params ? Gateway.ConstructUrl(endpoint) :
(() => {
let newUrl = new URL(Gateway.ConstructUrl(endpoint));
- newUrl.searchParams.append("data", data);
+ Object.getOwnPropertyNames(params).map(prop =>
+ newUrl.searchParams.append(prop, params[prop]));
return Gateway.ConstructUrl(endpoint) + newUrl.search;
- return newUrl as any;
})();
const response = await fetch(url,
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 4849ae9f7..d66c6e90f 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,10 +1,12 @@
import { action } from "mobx";
import { Document } from "../../fields/Document";
+import { emptyFunction } from "../../Utils";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
-import { CollectionView } from "../views/collections/CollectionView";
import { DocumentDecorations } from "../views/DocumentDecorations";
+import { Main } from "../views/Main";
import { DocumentView } from "../views/nodes/DocumentView";
-import { returnFalse } from "../../Utils";
+import globalStyles from "../views/_global_variables";
+// import globalStyleVariables from "../views/_global_variables.scss"; // bcz: why doesn't this work?
export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) {
let onRowMove = action((e: PointerEvent): void => {
@@ -147,6 +149,7 @@ export namespace DragManager {
dragDiv.className = "dragManager-dragDiv";
DragManager.Root().appendChild(dragDiv);
}
+ Main.Instance.SetTextDoc();
let scaleXs: number[] = [];
let scaleYs: number[] = [];
@@ -175,7 +178,7 @@ export namespace DragManager {
dragElement.style.bottom = "";
dragElement.style.left = "0";
dragElement.style.transformOrigin = "0 0";
- dragElement.style.zIndex = "1000";
+ dragElement.style.zIndex = globalStyles.contextMenuZindex;// "1000";
dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
dragElement.style.width = `${rect.width / scaleX}px`;
dragElement.style.height = `${rect.height / scaleY}px`;
@@ -224,7 +227,7 @@ export namespace DragManager {
CollectionDockingView.Instance.StartOtherDrag(docs, {
pageX: e.pageX,
pageY: e.pageY,
- preventDefault: () => { },
+ preventDefault: emptyFunction,
button: 0
});
}
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 290e26dc3..42bcf2ae2 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -224,12 +224,99 @@ export const marks: { [index: string]: MarkSpec } = {
},
+ /* FONTS */
timesNewRoman: {
parseDOM: [{ style: 'font-family: "Times New Roman", Times, serif;' }],
toDOM: () => ['span', {
style: 'font-family: "Times New Roman", Times, serif;'
}]
},
+
+ arial: {
+ parseDOM: [{ style: 'font-family: Arial, Helvetica, sans-serif;' }],
+ toDOM: () => ['span', {
+ style: 'font-family: Arial, Helvetica, sans-serif;'
+ }]
+ },
+
+ georgia: {
+ parseDOM: [{ style: 'font-family: Georgia, serif;' }],
+ toDOM: () => ['span', {
+ style: 'font-family: Georgia, serif;'
+ }]
+ },
+
+ comicSans: {
+ parseDOM: [{ style: 'font-family: "Comic Sans MS", cursive, sans-serif;' }],
+ toDOM: () => ['span', {
+ style: 'font-family: "Comic Sans MS", cursive, sans-serif;'
+ }]
+ },
+
+ tahoma: {
+ parseDOM: [{ style: 'font-family: Tahoma, Geneva, sans-serif;' }],
+ toDOM: () => ['span', {
+ style: 'font-family: Tahoma, Geneva, sans-serif;'
+ }]
+ },
+
+ impact: {
+ parseDOM: [{ style: 'font-family: Impact, Charcoal, sans-serif;' }],
+ toDOM: () => ['span', {
+ style: 'font-family: Impact, Charcoal, sans-serif;'
+ }]
+ },
+
+ /** FONT SIZES */
+
+ p10: {
+ parseDOM: [{ style: 'font-size: 10px;' }],
+ toDOM: () => ['span', {
+ style: 'font-size: 10px;'
+ }]
+ },
+
+ p12: {
+ parseDOM: [{ style: 'font-size: 12px;' }],
+ toDOM: () => ['span', {
+ style: 'font-size: 12px;'
+ }]
+ },
+
+ p16: {
+ parseDOM: [{ style: 'font-size: 16px;' }],
+ toDOM: () => ['span', {
+ style: 'font-size: 16px;'
+ }]
+ },
+
+ p24: {
+ parseDOM: [{ style: 'font-size: 24px;' }],
+ toDOM: () => ['span', {
+ style: 'font-size: 24px;'
+ }]
+ },
+
+ p32: {
+ parseDOM: [{ style: 'font-size: 32px;' }],
+ toDOM: () => ['span', {
+ style: 'font-size: 32px;'
+ }]
+ },
+
+ p48: {
+ parseDOM: [{ style: 'font-size: 48px;' }],
+ toDOM: () => ['span', {
+ style: 'font-size: 48px;'
+ }]
+ },
+
+ p72: {
+ parseDOM: [{ style: 'font-size: 72px;' }],
+ toDOM: () => ['span', {
+ style: 'font-size: 72px;'
+ }]
+ },
};
// :: Schema
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 5ddaafc72..2fa45a086 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -25,6 +25,7 @@ export namespace SelectionManager {
DeselectAll(): void {
manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false));
manager.SelectedDocuments = [];
+ Main.Instance.SetTextDoc();
}
}
@@ -48,7 +49,6 @@ export namespace SelectionManager {
manager.DeselectAll();
if (found) manager.SelectDoc(found, false);
- Main.Instance.SetTextDoc(undefined, undefined);
}
export function SelectedDocuments(): Array<DocumentView> {
diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss
index ea580d104..9111d60f1 100644
--- a/src/client/util/TooltipTextMenu.scss
+++ b/src/client/util/TooltipTextMenu.scss
@@ -1,8 +1,244 @@
@import "../views/global_variables";
+.ProseMirror-textblock-dropdown {
+ min-width: 3em;
+ }
+
+ .ProseMirror-menu {
+ margin: 0 -4px;
+ line-height: 1;
+ }
+
+ .ProseMirror-tooltip .ProseMirror-menu {
+ width: -webkit-fit-content;
+ width: fit-content;
+ white-space: pre;
+ }
+
+ .ProseMirror-menuitem {
+ margin-right: 3px;
+ display: inline-block;
+ }
+
+ .ProseMirror-menuseparator {
+ // border-right: 1px solid #ddd;
+ margin-right: 3px;
+ }
+
+ .ProseMirror-menu-dropdown, .ProseMirror-menu-dropdown-menu {
+ font-size: 90%;
+ white-space: nowrap;
+ }
+
+ .ProseMirror-menu-dropdown {
+ vertical-align: 1px;
+ cursor: pointer;
+ position: relative;
+ padding-right: 15px;
+ }
+
+ .ProseMirror-menu-dropdown-wrap {
+ padding: 1px 0 1px 4px;
+ display: inline-block;
+ position: relative;
+ }
+
+ .ProseMirror-menu-dropdown:after {
+ content: "";
+ border-left: 4px solid transparent;
+ border-right: 4px solid transparent;
+ border-top: 4px solid currentColor;
+ opacity: .6;
+ position: absolute;
+ right: 4px;
+ top: calc(50% - 2px);
+ }
+
+ .ProseMirror-menu-dropdown-menu, .ProseMirror-menu-submenu {
+ position: absolute;
+ background: $dark-color;
+ color:white;
+ border: 1px solid #aaa;
+ padding: 2px;
+ }
+
+ .ProseMirror-menu-dropdown-menu {
+ z-index: 15;
+ min-width: 6em;
+ }
+
+ .ProseMirror-menu-dropdown-item {
+ cursor: pointer;
+ padding: 2px 8px 2px 4px;
+ width: auto;
+ }
+
+ .ProseMirror-menu-dropdown-item:hover {
+ background: #2e2b2b;
+ }
+
+ .ProseMirror-menu-submenu-wrap {
+ position: relative;
+ margin-right: -4px;
+ }
+
+ .ProseMirror-menu-submenu-label:after {
+ content: "";
+ border-top: 4px solid transparent;
+ border-bottom: 4px solid transparent;
+ border-left: 4px solid currentColor;
+ opacity: .6;
+ position: absolute;
+ right: 4px;
+ top: calc(50% - 4px);
+ }
+
+ .ProseMirror-menu-submenu {
+ display: none;
+ min-width: 4em;
+ left: 100%;
+ top: -3px;
+ }
+
+ .ProseMirror-menu-active {
+ background: #eee;
+ border-radius: 4px;
+ }
+
+ .ProseMirror-menu-active {
+ background: #eee;
+ border-radius: 4px;
+ }
+
+ .ProseMirror-menu-disabled {
+ opacity: .3;
+ }
+
+ .ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu, .ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu {
+ display: block;
+ }
+
+ .ProseMirror-menubar {
+ border-top-left-radius: inherit;
+ border-top-right-radius: inherit;
+ position: relative;
+ min-height: 1em;
+ color: white;
+ padding: 1px 6px;
+ top: 0; left: 0; right: 0;
+ border-bottom: 1px solid silver;
+ background:$dark-color;
+ z-index: 10;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ overflow: visible;
+ }
+
+ .ProseMirror-icon {
+ display: inline-block;
+ line-height: .8;
+ vertical-align: -2px; /* Compensate for padding */
+ padding: 2px 8px;
+ cursor: pointer;
+ }
+
+ .ProseMirror-menu-disabled.ProseMirror-icon {
+ cursor: default;
+ }
+
+ .ProseMirror-icon svg {
+ fill: currentColor;
+ height: 1em;
+ }
+
+ .ProseMirror-icon span {
+ vertical-align: text-top;
+ }
+.ProseMirror-example-setup-style hr {
+ padding: 2px 10px;
+ border: none;
+ margin: 1em 0;
+ }
+
+ .ProseMirror-example-setup-style hr:after {
+ content: "";
+ display: block;
+ height: 1px;
+ background-color: silver;
+ line-height: 2px;
+ }
+
+ .ProseMirror ul, .ProseMirror ol {
+ padding-left: 30px;
+ }
+
+ .ProseMirror blockquote {
+ padding-left: 1em;
+ border-left: 3px solid #eee;
+ margin-left: 0; margin-right: 0;
+ }
+
+ .ProseMirror-example-setup-style img {
+ cursor: default;
+ }
+
+ .ProseMirror-prompt {
+ background: white;
+ padding: 5px 10px 5px 15px;
+ border: 1px solid silver;
+ position: fixed;
+ border-radius: 3px;
+ z-index: 11;
+ box-shadow: -.5px 2px 5px rgba(0, 0, 0, .2);
+ }
+
+ .ProseMirror-prompt h5 {
+ margin: 0;
+ font-weight: normal;
+ font-size: 100%;
+ color: #444;
+ }
+
+ .ProseMirror-prompt input[type="text"],
+ .ProseMirror-prompt textarea {
+ background: #eee;
+ border: none;
+ outline: none;
+ }
+
+ .ProseMirror-prompt input[type="text"] {
+ padding: 0 4px;
+ }
+
+ .ProseMirror-prompt-close {
+ position: absolute;
+ left: 2px; top: 1px;
+ color: #666;
+ border: none; background: transparent; padding: 0;
+ }
+
+ .ProseMirror-prompt-close:after {
+ content: "✕";
+ font-size: 12px;
+ }
+
+ .ProseMirror-invalid {
+ background: #ffc;
+ border: 1px solid #cc7;
+ border-radius: 4px;
+ padding: 5px 10px;
+ position: absolute;
+ min-width: 10em;
+ }
+
+ .ProseMirror-prompt-buttons {
+ margin-top: 5px;
+ display: none;
+ }
+
.tooltipMenu {
position: absolute;
- z-index: 20;
+ z-index: 200;
background: $dark-color;
border: 1px solid silver;
border-radius: 4px;
@@ -39,7 +275,7 @@
display: inline-block;
border-right: 1px solid rgba(0, 0, 0, 0.2);
//color: rgb(19, 18, 18);
- color: $light-color;
+ color: white;
line-height: 1;
padding: 0px 2px;
margin: 1px;
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 48f835a0f..3869db41a 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -1,19 +1,23 @@
import { action, IReactionDisposer, reaction } from "mobx";
-import { Dropdown, DropdownSubmenu, MenuItem } from "prosemirror-menu";
+import { Dropdown, DropdownSubmenu, MenuItem, MenuItemSpec, renderGrouped, icons, } from "prosemirror-menu"; //no import css
import { baseKeymap, lift } from "prosemirror-commands";
import { history, redo, undo } from "prosemirror-history";
import { keymap } from "prosemirror-keymap";
-import { EditorState, Transaction, NodeSelection } from "prosemirror-state";
+import { EditorState, Transaction, NodeSelection, TextSelection } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { schema } from "./RichTextSchema";
-import { Schema, NodeType } from "prosemirror-model";
+import { Schema, NodeType, MarkType } from "prosemirror-model";
import React = require("react");
import "./TooltipTextMenu.scss";
const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands");
import { library } from '@fortawesome/fontawesome-svg-core';
import { wrapInList, bulletList, liftListItem, listItem } from 'prosemirror-schema-list';
-import { faListUl } from '@fortawesome/free-solid-svg-icons';
+import {
+ faListUl,
+} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { FieldViewProps } from "../views/nodes/FieldView";
+import { throwStatement } from "babel-types";
const SVG = "http://www.w3.org/2000/svg";
@@ -22,8 +26,14 @@ export class TooltipTextMenu {
private tooltip: HTMLElement;
private num_icons = 0;
-
- constructor(view: EditorView) {
+ private view: EditorView;
+ private fontStyles: MarkType[];
+ private fontSizes: MarkType[];
+ private editorProps: FieldViewProps;
+
+ constructor(view: EditorView, editorProps: FieldViewProps) {
+ this.view = view;
+ this.editorProps = editorProps;
this.tooltip = document.createElement("div");
this.tooltip.className = "tooltipMenu";
@@ -32,7 +42,6 @@ export class TooltipTextMenu {
//add additional icons
library.add(faListUl);
-
//add the buttons to the tooltip
let items = [
{ command: toggleMark(schema.marks.strong), dom: this.icon("B", "strong") },
@@ -42,44 +51,142 @@ export class TooltipTextMenu {
{ command: toggleMark(schema.marks.superscript), dom: this.icon("s", "superscript") },
{ command: toggleMark(schema.marks.subscript), dom: this.icon("s", "subscript") },
{ command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") },
- { command: toggleMark(schema.marks.timesNewRoman), dom: this.icon("x", "TNR") },
{ command: lift, dom: this.icon("<", "lift") },
];
//add menu items
items.forEach(({ dom, command }) => {
this.tooltip.appendChild(dom);
- });
- //add dropdowns
-
- //pointer down handler to activate button effects
- this.tooltip.addEventListener("pointerdown", e => {
- e.preventDefault();
- view.focus();
- //update view of icons
- this.num_icons = 0;
- items.forEach(({ command, dom }) => {
- if (e.srcElement && dom.contains(e.srcElement as Node)) {
- //let active = command(view.state, view.dispatch, view);
- let active = command(view.state, view.dispatch, view);
- //uncomment this if we want the bullet button to disappear if current selection is bulleted
- //dom.style.display = active ? "" : "none";
- }
+ //pointer down handler to activate button effects
+ dom.addEventListener("pointerdown", e => {
+ e.preventDefault();
+ view.focus();
+ command(view.state, view.dispatch, view);
});
+
});
+ //dropdowns
+ //list of font btns to add
+ this.fontStyles = [
+ schema.marks.timesNewRoman,
+ schema.marks.arial,
+ schema.marks.georgia,
+ schema.marks.comicSans,
+ schema.marks.tahoma,
+ schema.marks.impact,
+ ];
+ this.fontSizes = [
+ schema.marks.p10,
+ schema.marks.p12,
+ schema.marks.p16,
+ schema.marks.p24,
+ schema.marks.p32,
+ schema.marks.p48,
+ schema.marks.p72,
+ ];
+ this.addFontDropdowns();
+
this.update(view, undefined);
}
+ //adds font size and font style dropdowns
+ addFontDropdowns() {
+ //filtering function - might be unecessary
+ let cut = (arr: MenuItem[]) => arr.filter(x => x);
+ let fontBtns = [
+ this.dropdownBtn("Times New Roman", "font-family: Times New Roman, Times, serif; width: 120px;", schema.marks.timesNewRoman, this.view, this.changeToMarkInGroup, this.fontStyles),
+ this.dropdownBtn("Arial", "font-family: Arial, Helvetica, sans-serif; width: 120px;", schema.marks.arial, this.view, this.changeToMarkInGroup, this.fontStyles),
+ this.dropdownBtn("Georgia", "font-family: Georgia, serif; width: 120px; width: 120px;", schema.marks.georgia, this.view, this.changeToMarkInGroup, this.fontStyles),
+ this.dropdownBtn("ComicSans", "font-family: Comic Sans MS, cursive, sans-serif; width: 120px;", schema.marks.comicSans, this.view, this.changeToMarkInGroup, this.fontStyles),
+ this.dropdownBtn("Tahoma", "font-family: Tahoma, Geneva, sans-serif; width: 120px;", schema.marks.tahoma, this.view, this.changeToMarkInGroup, this.fontStyles),
+ this.dropdownBtn("Impact", "font-family: Impact, Charcoal, sans-serif; width: 120px;", schema.marks.impact, this.view, this.changeToMarkInGroup, this.fontStyles),
+ ];
+
+ let fontSizeBtns = [
+ this.dropdownBtn("10", "width: 50px;", schema.marks.p10, this.view, this.changeToMarkInGroup, this.fontSizes),
+ this.dropdownBtn("12", "width: 50px;", schema.marks.p12, this.view, this.changeToMarkInGroup, this.fontSizes),
+ this.dropdownBtn("16", "width: 50px;", schema.marks.p16, this.view, this.changeToMarkInGroup, this.fontSizes),
+ this.dropdownBtn("24", "width: 50px;", schema.marks.p24, this.view, this.changeToMarkInGroup, this.fontSizes),
+ this.dropdownBtn("32", "width: 50px;", schema.marks.p32, this.view, this.changeToMarkInGroup, this.fontSizes),
+ this.dropdownBtn("48", "width: 50px;", schema.marks.p48, this.view, this.changeToMarkInGroup, this.fontSizes),
+ this.dropdownBtn("72", "width: 50px;", schema.marks.p72, 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;" }) as MenuItem;
+ this.tooltip.appendChild(dd_fontStyle.render(this.view).dom);
+ this.tooltip.appendChild(dd_fontSize.render(this.view).dom);
+ }
+
+ //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text
+ changeToMarkInGroup(markType: MarkType, view: EditorView, fontMarks: MarkType[]) {
+ let { empty, $cursor, ranges } = view.state.selection as TextSelection;
+ let state = view.state;
+ let dispatch = view.dispatch;
+
+ //remove all other active font marks
+ fontMarks.forEach((type) => {
+ if (dispatch) {
+ if ($cursor) {
+ if (type.isInSet(state.storedMarks || $cursor.marks())) {
+ dispatch(state.tr.removeStoredMark(type));
+ }
+ } else {
+ let has = false, tr = state.tr;
+ for (let i = 0; !has && i < ranges.length; i++) {
+ let { $from, $to } = ranges[i];
+ has = state.doc.rangeHasMark($from.pos, $to.pos, type);
+ }
+ for (let i of ranges) {
+ let { $from, $to } = i;
+ if (has) {
+ toggleMark(type)(view.state, view.dispatch, view);
+ }
+ }
+ }
+ }
+ }); //actually apply font
+ return toggleMark(markType)(view.state, view.dispatch, view);
+ }
+
+ //makes a button for the drop down
+ //css is the style you want applied to the button
+ dropdownBtn(label: string, css: string, markType: MarkType, view: EditorView, changeToMarkInGroup: (markType: MarkType<any>, view: EditorView, groupMarks: MarkType[]) => any, groupMarks: MarkType[]) {
+ return new MenuItem({
+ title: "",
+ label: label,
+ execEvent: "",
+ class: "menuicon",
+ css: css,
+ enable(state) { return true; },
+ run() {
+ changeToMarkInGroup(markType, view, groupMarks);
+ }
+ });
+ }
// Helper function to create menu icons
icon(text: string, name: string) {
let span = document.createElement("span");
span.className = "menuicon " + name;
span.title = name;
span.textContent = text;
+ span.style.color = "white";
return span;
}
+ //method for checking whether node can be inserted
+ canInsert(state: EditorState, nodeType: NodeType<Schema<string, string>>) {
+ let $from = state.selection.$from;
+ for (let d = $from.depth; d >= 0; d--) {
+ let index = $from.index(d);
+ if ($from.node(d).canReplaceWith(index, index, nodeType)) return true;
+ }
+ return false;
+ }
+
+
//adapted this method - use it to check if block has a tag (ie bulleting)
blockActive(type: NodeType<Schema<string, string>>, state: EditorState) {
let attrs = {};
@@ -98,30 +205,6 @@ export class TooltipTextMenu {
}
}
- //this doesn't currently work but could be used to use icons for buttons
- unorderedListIcon(): HTMLSpanElement {
- let span = document.createElement("span");
- //let icon = document.createElement("FontAwesomeIcon");
- //icon.className = "menuicon";
- //icon.style.color = "white";
- //span.appendChild(<i style={{ color: "white" }} icon="list-ul" size="lg" />);
- let i = document.createElement("i");
- i.className = "fa falist-ul";
- span.appendChild(i);
- //span.appendChild(icon);
- //return liftItem.spec.icon.sty
-
- //let sym = document.createElementNS(SVG, "symbol")
- // sym.id = name
- //sym.style.color = "white";
- //width then height
- //sym.setAttribute("viewBox", "0 0 " + 1024 + " " + 1024);
- //let path = sym.appendChild(document.createElementNS(SVG, "path"));
- //path.setAttribute("d", "M219 310v329q0 7-5 12t-12 5q-8 0-13-5l-164-164q-5-5-5-13t5-13l164-164q5-5 13-5 7 0 12 5t5 12zM1024 749v109q0 7-5 12t-12 5h-987q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h987q7 0 12 5t5 12zM1024 530v109q0 7-5 12t-12 5h-621q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h621q7 0 12 5t5 12zM1024 310v109q0 7-5 12t-12 5h-621q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h621q7 0 12 5t5 12zM1024 91v109q0 7-5 12t-12 5h-987q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h987q7 0 12 5t5 12z");
- //span.appendChild(sym);
- return span;
- }
-
// Create an icon for a heading at the given level
heading(level: number) {
return {
@@ -152,14 +235,12 @@ export class TooltipTextMenu {
// Find a center-ish x position from the selection endpoints (when
// crossing lines, end may be more to the left)
let left = Math.max((start.left + end.left) / 2, start.left + 3);
- this.tooltip.style.left = (left - box.left) + "px";
- //let width = Math.abs(start.left - end.left) / 2;
- let width = 8 * 16 + 15;
+ this.tooltip.style.left = (left - box.left) * this.editorProps.ScreenToLocalTransform().Scale + "px";
+ let width = Math.abs(start.left - end.left) / 2 * this.editorProps.ScreenToLocalTransform().Scale;
let mid = Math.min(start.left, end.left) + width;
- //THIS WIDTH IS 15 * NUMBER OF ICONS + 15
- this.tooltip.style.width = width + "px";
- this.tooltip.style.bottom = (box.bottom - start.top) + "px";
+ this.tooltip.style.width = 220 + "px";
+ this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px";
}
destroy() { this.tooltip.remove(); }
diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss
index f6830d9cd..5acf598cf 100644
--- a/src/client/views/ContextMenu.scss
+++ b/src/client/views/ContextMenu.scss
@@ -2,7 +2,7 @@
.contextMenu-cont {
position: absolute;
display: flex;
- z-index: 1000;
+ z-index: $contextMenu-zindex;
box-shadow: $intermediate-color 0.2vw 0.2vw 0.4vw;
flex-direction: column;
}
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index c4e4aed8e..321bda384 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -1,11 +1,14 @@
@import "global_variables";
+.documentDecorations {
+ position: absolute;
+}
#documentDecorations-container {
position: absolute;
top: 0;
left:0;
display: grid;
- z-index: 1000;
+ z-index: $docDecorations-zindex;
grid-template-rows: 20px 8px 1fr 8px;
grid-template-columns: 8px 8px 1fr 8px 8px;
pointer-events: none;
@@ -90,37 +93,6 @@
opacity: 0.1;
}
-// position: absolute;
-// display: grid;
-// z-index: 1000;
-// grid-template-rows: 20px 1fr 20px 0px;
-// grid-template-columns: 20px 1fr 20px;
-// pointer-events: none;
-// #documentDecorations-centerCont {
-// background: none;
-// }
-// .documentDecorations-resizer {
-// pointer-events: auto;
-// background: lightblue;
-// opacity: 0.4;
-// }
-// #documentDecorations-topLeftResizer,
-// #documentDecorations-bottomRightResizer {
-// cursor: nwse-resize;
-// }
-// #documentDecorations-topRightResizer,
-// #documentDecorations-bottomLeftResizer {
-// cursor: nesw-resize;
-// }
-// #documentDecorations-topResizer,
-// #documentDecorations-bottomResizer {
-// cursor: ns-resize;
-// }
-// #documentDecorations-leftResizer,
-// #documentDecorations-rightResizer {
-// cursor: ew-resize;
-// }
-// }
.linkFlyout {
grid-column: 1/4;
margin-left: 25px;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 28af46358..29cca286d 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -15,6 +15,8 @@ import { DocumentView } from "./nodes/DocumentView";
import { LinkMenu } from "./nodes/LinkMenu";
import React = require("react");
import { FieldWaiting } from "../../fields/Field";
+import { emptyFunction } from "../../Utils";
+import { Main } from "./Main";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -217,7 +219,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
let dragData = new DragManager.LinkDragData(SelectionManager.SelectedDocuments()[0]);
DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, {
handlers: {
- dragComplete: action(() => { }),
+ dragComplete: action(emptyFunction),
},
hideSource: false
});
@@ -262,7 +264,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs);
DragManager.StartDocumentDrag([this._linkButton.current], dragData, e.x, e.y, {
handlers: {
- dragComplete: action(() => { }),
+ dragComplete: action(emptyFunction),
},
hideSource: false
});
@@ -320,6 +322,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
break;
}
+ Main.Instance.SetTextDoc();
SelectionManager.SelectedDocuments().forEach(element => {
const rect = element.screenRect();
if (rect.width !== 0) {
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index fe7f007b0..7329b8eb6 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -169,21 +169,22 @@ button:hover {
overflow: scroll;
}
.mainDiv-textInput {
- background:pink;
- width: 200px;
- height: 200px;
+ background-color: rgba(248, 6, 6, 0.001);
+ width: 200px;
+ height: 200px;
+ position:absolute;
+ overflow: visible;
+ top: 0;
+ left: 0;
+ z-index: $mainTextInput-zindex;
+ .formattedTextBox-cont {
+ background-color: rgba(248, 6, 6, 0.001);
+ width: 100%;
+ height: 100%;
position:absolute;
- overflow: visible;
top: 0;
left: 0;
- .formattedTextBox-cont {
- background:pink;
- width: 100%;
- height: 100%;
- position:absolute;
- top: 0;
- left: 0;
- }
+}
}
#mainContent-div {
width:100%;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 6e7701d89..c4c4a6bf9 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -23,12 +23,12 @@ import { Documents } from '../documents/Documents';
import { ColumnAttributeModel } from '../northstar/core/attribute/AttributeModel';
import { AttributeTransformationModel } from '../northstar/core/attribute/AttributeTransformationModel';
import { Gateway, Settings } from '../northstar/manager/Gateway';
-import { AggregateFunction, Catalog } from '../northstar/model/idea/idea';
+import { AggregateFunction, Catalog, Point } from '../northstar/model/idea/idea';
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,8 @@ import { InkingControl } from './InkingControl';
import "./Main.scss";
import { DocumentView } from './nodes/DocumentView';
import { FormattedTextBox } from './nodes/FormattedTextBox';
+import { REPLCommand } from 'repl';
+import { Key } from '../../fields/Key';
@observer
export class Main extends React.Component {
@@ -66,6 +68,7 @@ export class Main extends React.Component {
constructor(props: Readonly<{}>) {
super(props);
+ this._textProxyDiv = React.createRef();
Main.Instance = this;
// causes errors to be generated when modifying an observable outside of an action
configure({ enforceActions: "observed" });
@@ -204,15 +207,67 @@ export class Main extends React.Component {
@observable _textDoc?: Document = undefined;
_textRect: any;
+ _textXf: Transform = Transform.Identity();
+ _textScroll: number = 0;
+ _textFieldKey: Key = KeyStore.Data;
+ _textColor: string | null = null;
+ _textTargetDiv: HTMLDivElement | undefined;
+ _textProxyDiv: React.RefObject<HTMLDivElement>;
@action
- SetTextDoc(textDoc?: Document, div?: HTMLDivElement) {
+ SetTextDoc(textDoc?: Document, textFieldKey?: Key, div?: HTMLDivElement, tx?: Transform) {
+ if (this._textTargetDiv) {
+ this._textTargetDiv.style.color = this._textColor;
+ }
+
this._textDoc = undefined;
this._textDoc = textDoc;
+ this._textFieldKey = textFieldKey!;
+ this._textXf = tx ? tx : Transform.Identity();
+ this._textTargetDiv = div;
if (div) {
+ this._textColor = div.style.color;
+ div.style.color = "transparent";
this._textRect = div.getBoundingClientRect();
+ this._textScroll = div.scrollTop;
+ }
+ }
+
+ @action
+ textScroll = (e: React.UIEvent) => {
+ if (this._textProxyDiv.current && this._textTargetDiv) {
+ this._textTargetDiv.scrollTop = this._textScroll = this._textProxyDiv.current.children[0].scrollTop;
}
}
+ textBoxDown = (e: React.PointerEvent) => {
+ if (e.button !== 0 || e.metaKey || e.altKey) {
+ document.addEventListener("pointermove", this.textBoxMove);
+ document.addEventListener('pointerup', this.textBoxUp);
+ }
+ }
+ textBoxMove = (e: PointerEvent) => {
+ if (e.movementX > 1 || e.movementY > 1) {
+ document.removeEventListener("pointermove", this.textBoxMove);
+ document.removeEventListener('pointerup', this.textBoxUp);
+ let dragData = new DragManager.DocumentDragData([this._textDoc!]);
+ const [left, top] = this._textXf
+ .inverse()
+ .transformPoint(0, 0);
+ dragData.xOffset = e.clientX - left;
+ dragData.yOffset = e.clientY - top;
+ DragManager.StartDocumentDrag([this._textTargetDiv!], dragData, e.clientX, e.clientY, {
+ handlers: {
+ dragComplete: action(emptyFunction),
+ },
+ hideSource: false
+ });
+ }
+ }
+ textBoxUp = (e: PointerEvent) => {
+ document.removeEventListener("pointermove", this.textBoxMove);
+ document.removeEventListener('pointerup', this.textBoxUp);
+ }
+
@computed
get activeTextBox() {
if (this._textDoc) {
@@ -220,8 +275,13 @@ export class Main extends React.Component {
let y: number = this._textRect.y;
let w: number = this._textRect.width;
let h: number = this._textRect.height;
- return <div className="mainDiv-textInput" style={{ transform: `translate(${x}px, ${y}px)`, width: `${w}px`, height: `${h}px` }} >
- <FormattedTextBox fieldKey={KeyStore.Archives} Document={this._textDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={Transform.Identity} focus={(doc) => { }} />
+ let t = this._textXf.transformPoint(0, 0);
+ let s = this._textXf.transformPoint(1, 0);
+ s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1]));
+ return <div className="mainDiv-textInput" style={{ pointerEvents: "none", transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
+ <div className="mainDiv-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} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={(doc) => { }} />
+ </div>
</ div>;
}
else return (null);
@@ -310,7 +370,7 @@ export class Main extends React.Component {
<div className="main-buttonDiv" key="workspaces" style={{ top: '34px', left: '2px', position: 'absolute' }} ref={workspacesRef}>
<button onClick={this.toggleWorkspaces}>Workspaces</button></div>,
<div className="main-buttonDiv" key="logout" style={{ top: '34px', right: '1px', position: 'absolute' }} ref={logoutRef}>
- <button onClick={() => request.get(ServerUtils.prepend(RouteStore.logout), () => { })}>Log Out</button></div>
+ <button onClick={() => request.get(ServerUtils.prepend(RouteStore.logout), emptyFunction)}>Log Out</button></div>
];
}
@@ -384,9 +444,8 @@ export class Main extends React.Component {
let cat = Gateway.Instance.ClearCatalog();
cat.then(async () => {
this.AddToNorthstarCatalog(await Gateway.Instance.GetCatalog());
- if (!CurrentUserUtils.GetNorthstarSchema("Book1")) {
- this.AddToNorthstarCatalog(await Gateway.Instance.GetSchema("http://www.cs.brown.edu/~bcz/Book1.csv"));
- }
+ // if (!CurrentUserUtils.GetNorthstarSchema("Book1"))
+ // this.AddToNorthstarCatalog(await Gateway.Instance.GetSchema("http://www.cs.brown.edu/~bcz/Book1.csv", "Book1"));
});
}
diff --git a/src/client/views/_global_variables.scss b/src/client/views/_global_variables.scss
index 44a819b79..cd6af2dac 100644
--- a/src/client/views/_global_variables.scss
+++ b/src/client/views/_global_variables.scss
@@ -15,3 +15,14 @@ $sans-serif: "Noto Sans", sans-serif;
$serif: "Crimson Text", serif;
// misc values
$border-radius: 0.3em;
+//
+
+ // dragged items
+$contextMenu-zindex: 1000; // context menu shows up over everything
+$mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc
+$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?
+
+:export {
+ contextMenuZindex: $contextMenu-zindex;
+} \ No newline at end of file
diff --git a/src/client/views/_global_variables.scss.d.ts b/src/client/views/_global_variables.scss.d.ts
new file mode 100644
index 000000000..c902d473f
--- /dev/null
+++ b/src/client/views/_global_variables.scss.d.ts
@@ -0,0 +1,7 @@
+
+export interface I_globalScss {
+ contextMenuZindex: string; // context menu shows up over everything
+}
+export const globalStyleVariables: I_globalScss;
+
+export default globalStyleVariables; \ No newline at end of file
diff --git a/src/client/views/_global_variables.ts b/src/client/views/_global_variables.ts
new file mode 100644
index 000000000..e70bfd56c
--- /dev/null
+++ b/src/client/views/_global_variables.ts
@@ -0,0 +1,8 @@
+import * as globalStyleVariables from "../views/_global_variables.scss"
+
+export interface I_globalScss {
+ contextMenuZindex: string; // context menu shows up over everything
+}
+let globalStyles = globalStyleVariables as any as I_globalScss;
+
+export default globalStyles; \ No newline at end of file
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index ea6d3a247..212cf8a69 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -50,7 +50,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
public StartOtherDrag(dragDocs: Document[], e: any) {
dragDocs.map(dragDoc =>
this.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener.
- onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: () => { }, button: 0 }));
+ onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }));
}
@action
@@ -204,7 +204,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), e.pageX, e.pageY,
{
handlers: {
- dragComplete: action(() => { }),
+ dragComplete: action(emptyFunction),
},
hideSource: false
});
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 97bac745c..6cbe59012 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -7,6 +7,7 @@ import React = require("react");
import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
import { FieldView, FieldViewProps } from "../nodes/FieldView";
import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView";
+import { emptyFunction } from "../../../Utils";
@observer
@@ -33,7 +34,7 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
onContextMenu = (e: React.MouseEvent): void => {
if (!e.isPropagationStopped() && this.props.Document.Id !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
- ContextMenu.Instance.addItem({ description: "PDFOptions", event: () => { } });
+ ContextMenu.Instance.addItem({ description: "PDFOptions", event: emptyFunction });
}
}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 587f60b3d..f1b3e1b8f 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -77,8 +77,8 @@ export class CollectionSchemaView extends CollectionSubView {
let props: FieldViewProps = {
Document: rowProps.value[0],
fieldKey: rowProps.value[1],
- isSelected: () => false,
- select: () => { },
+ isSelected: returnFalse,
+ select: emptyFunction,
isTopMost: false,
selectOnLoad: false,
ScreenToLocalTransform: Transform.Identity,
@@ -254,7 +254,7 @@ export class CollectionSchemaView extends CollectionSubView {
focusDocument = (doc: Document) => { };
onPointerDown = (e: React.PointerEvent): void => {
- if (this.props.isSelected()) {
+ if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
e.stopPropagation();
}
}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5cdea0568..6a6a6c900 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -15,6 +15,7 @@ import { ServerUtils } from "../../../server/ServerUtil";
import { Server } from "../../Server";
import { FieldViewProps } from "../nodes/FieldView";
import * as rp from 'request-promise';
+import { emptyFunction } from "../../../Utils";
export interface CollectionViewProps extends FieldViewProps {
addDocument: (document: Document, allowDuplicates?: boolean) => boolean;
@@ -74,12 +75,21 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
}
let added = false;
if (de.data.aliasOnDrop || de.data.copyOnDrop) {
- added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false);
+ added = de.data.droppedDocuments.reduce((added: boolean, d) => {
+ let moved = this.props.addDocument(d);
+ return moved || added;
+ }, false);
} else if (de.data.moveDocument) {
const move = de.data.moveDocument;
- added = de.data.droppedDocuments.reduce((added: boolean, d) => added || move(d, this.props.Document, this.props.addDocument), false);
+ added = de.data.droppedDocuments.reduce((added: boolean, d) => {
+ let moved = move(d, this.props.Document, this.props.addDocument);
+ return moved || added;
+ }, false);
} else {
- added = de.data.droppedDocuments.reduce((added: boolean, d) => added || this.props.addDocument(d), false);
+ added = de.data.droppedDocuments.reduce((added: boolean, d) => {
+ let moved = this.props.addDocument(d);
+ return moved || added;
+ }, false);
}
e.stopPropagation();
return added;
@@ -87,8 +97,8 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
return false;
}
- protected getDocumentFromType(type: string, path: string, options: DocumentOptions): Opt<Document> {
- let ctor: ((path: string, options: DocumentOptions) => Document) | undefined;
+ protected async getDocumentFromType(type: string, path: string, options: DocumentOptions): Promise<Opt<Document>> {
+ let ctor: ((path: string, options: DocumentOptions) => (Document | Promise<Document | undefined>)) | undefined = undefined;
if (type.indexOf("image") !== -1) {
ctor = Documents.ImageDocument;
}
@@ -102,6 +112,10 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
ctor = Documents.PdfDocument;
options.nativeWidth = 1200;
}
+ if (type.indexOf("excel") !== -1) {
+ ctor = Documents.DBDocument;
+ options.copyDraggedItems = true;
+ }
if (type.indexOf("html") !== -1) {
if (path.includes('localhost')) {
let s = path.split('/');
@@ -159,10 +173,11 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
})).then(res => {
let type = res.headers["content-type"];
if (type) {
- let doc = this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 });
- if (doc) {
- this.props.addDocument(doc, false);
- }
+ this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 }).then(doc => {
+ if (doc) {
+ this.props.addDocument(doc, false);
+ }
+ });
}
});
promises.push(prom);
@@ -176,6 +191,7 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
if (file) {
formData.append('file', file);
}
+ let dropFileName = file ? file.name : "-empty-";
let prom = fetch(upload, {
method: 'POST',
@@ -185,18 +201,20 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
json.map((file: any) => {
let path = window.location.origin + file;
runInAction(() => {
- let doc = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300 });
+ let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName });
- let docs = this.props.Document.GetT(KeyStore.Data, ListField);
- if (docs !== FieldWaiting) {
- if (!docs) {
- docs = new ListField<Document>();
- this.props.Document.Set(KeyStore.Data, docs);
- }
- if (doc) {
- docs.Data.push(doc);
+ docPromise.then(doc => runInAction(() => {
+ let docs = this.props.Document.GetT(KeyStore.Data, ListField);
+ if (docs !== FieldWaiting) {
+ if (!docs) {
+ docs = new ListField<Document>();
+ this.props.Document.Set(KeyStore.Data, docs);
+ }
+ if (doc) {
+ docs.Data.push(doc);
+ }
}
- }
+ }));
});
});
});
@@ -205,7 +223,7 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
}
if (promises.length) {
- Promise.all(promises).catch(() => { }).then(() => batch.end());
+ Promise.all(promises).catch(emptyFunction).then(() => batch.end());
} else {
batch.end();
}
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index b02983a2e..6c9780adb 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -7,6 +7,7 @@ import React = require("react");
import "./CollectionVideoView.scss";
import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
import { FieldView, FieldViewProps } from "../nodes/FieldView";
+import { emptyFunction } from "../../../Utils";
@observer
@@ -100,7 +101,7 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
onContextMenu = (e: React.MouseEvent): void => {
if (!e.isPropagationStopped() && this.props.Document.Id !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
- ContextMenu.Instance.addItem({ description: "VideoOptions", event: () => { } });
+ ContextMenu.Instance.addItem({ description: "VideoOptions", event: emptyFunction });
}
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
new file mode 100644
index 000000000..c38787802
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
@@ -0,0 +1,24 @@
+@import "global_variables";
+
+.collectionFreeFormRemoteCursors-cont {
+
+ position:absolute;
+ z-index: $remoteCursors-zindex;
+ transform-origin: 'center center';
+}
+.collectionFreeFormRemoteCursors-canvas {
+
+ position:absolute;
+ width: 20px;
+ height: 20px;
+ opacity: 0.5;
+ border-radius: 50%;
+ border: 2px solid black;
+}
+.collectionFreeFormRemoteCursors-symbol {
+ font-size: 14;
+ color: black;
+ // fontStyle: "italic",
+ margin-left: -12;
+ margin-top: 4;
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index fc832264d..751ea8190 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -59,37 +59,17 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
let point = entry.Data[1];
this.drawCrosshairs("#" + v5(id, v5.URL).substring(0, 6).toUpperCase() + "22");
return (
- <div
- key={id}
- style={{
- position: "absolute",
- transform: `translate(${point[0] - 10}px, ${point[1] - 10}px)`,
- zIndex: 10000,
- transformOrigin: 'center center',
- }}
+ <div key={id} className="collectionFreeFormRemoteCursors-cont"
+ style={{ transform: `translate(${point[0] - 10}px, ${point[1] - 10}px)` }}
>
- <canvas
+ <canvas className="collectionFreeFormRemoteCursors-canvas"
ref={(el) => { if (el) this.crosshairs = el; }}
width={20}
height={20}
- style={{
- position: 'absolute',
- width: "20px",
- height: "20px",
- opacity: 0.5,
- borderRadius: "50%",
- border: "2px solid black"
- }}
/>
- <p
- style={{
- fontSize: 14,
- color: "black",
- // fontStyle: "italic",
- marginLeft: -12,
- marginTop: 4
- }}
- >{email[0].toUpperCase()}</p>
+ <p className="collectionFreeFormRemoteCursors-symbol">
+ {email[0].toUpperCase()}
+ </p>
</div>
);
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 81f2146e4..31809f30b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -46,6 +46,7 @@
}
.formattedTextBox-cont {
background: $light-color-secondary;
+ overflow: visible;
}
opacity: 0.99;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 9914f3793..01ebbe0e1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -25,6 +25,7 @@ import { SelectionManager } from "../../../util/SelectionManager";
import { NumberField } from "../../../../fields/NumberField";
import { Main } from "../../Main";
import Measure from "react-measure";
+import { returnFalse, emptyFunction } from "../../../../Utils";
@observer
export class CollectionFreeFormView extends CollectionSubView {
@@ -134,9 +135,6 @@ export class CollectionFreeFormView extends CollectionSubView {
document.addEventListener("pointerup", this.onPointerUp);
this._lastX = this.DownX = e.pageX;
this._lastY = this.DownY = e.pageY;
- if (this.props.isSelected()) {
- e.stopPropagation();
- }
}
}
@@ -200,7 +198,7 @@ export class CollectionFreeFormView extends CollectionSubView {
@action
private SetPan(panX: number, panY: number) {
- Main.Instance.SetTextDoc(undefined, undefined);
+ Main.Instance.SetTextDoc();
var x1 = this.getLocalTransform().inverse().Scale;
const newPanX = Math.min((1 - 1 / x1) * this.nativeWidth, Math.max(0, panX));
const newPanY = Math.min((1 - 1 / x1) * this.nativeHeight, Math.max(0, panY));
@@ -290,13 +288,13 @@ export class CollectionFreeFormView extends CollectionSubView {
get backgroundView() {
return !this.backgroundLayout ? (null) :
(<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)}
- layoutKey={KeyStore.BackgroundLayout} isTopMost={this.props.isTopMost} isSelected={() => false} select={() => { }} />);
+ layoutKey={KeyStore.BackgroundLayout} isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
}
@computed
get overlayView() {
return !this.overlayLayout ? (null) :
(<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)}
- layoutKey={KeyStore.OverlayLayout} isTopMost={this.props.isTopMost} isSelected={() => false} select={() => { }} />);
+ layoutKey={KeyStore.OverlayLayout} isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
}
getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform());
@@ -306,10 +304,12 @@ export class CollectionFreeFormView extends CollectionSubView {
childViews = () => this.views;
render() {
- let [dx, dy] = [this.centeringShiftX, this.centeringShiftY];
-
+ const [dx, dy] = [this.centeringShiftX, this.centeringShiftY];
const panx: number = -this.props.Document.GetNumber(KeyStore.PanX, 0);
const pany: number = -this.props.Document.GetNumber(KeyStore.PanY, 0);
+ const zoom: number = this.zoomScaling;
+ const blay = this.backgroundView;
+ const olay = this.overlayView;
return (
<Measure onResize={(r: any) => runInAction(() => { this._pwidth = r.entry.width; this._pheight = r.entry.height; })}>
@@ -325,8 +325,8 @@ export class CollectionFreeFormView extends CollectionSubView {
<PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox}
getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} >
<div className="collectionfreeformview" ref={this._canvasRef}
- style={{ transform: `translate(${dx}px, ${dy}px) scale(${this.zoomScaling}, ${this.zoomScaling}) translate(${panx}px, ${pany}px)` }}>
- {this.backgroundView}
+ style={{ transform: `translate(${dx}px, ${dy}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}>
+ {blay}
<CollectionFreeFormLinksView {...this.props}>
<InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
{this.childViews}
@@ -334,7 +334,7 @@ export class CollectionFreeFormView extends CollectionSubView {
</CollectionFreeFormLinksView>
<CollectionFreeFormRemoteCursors {...this.props} />
</div>
- {this.overlayView}
+ {olay}
</PreviewCursor>
</MarqueeView>
</div>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9670ca6b2..9c31a83c1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, IReactionDisposer, reaction, runInAction } from "mobx";
+import { action, computed, IReactionDisposer, reaction, runInAction, trace } from "mobx";
import { observer } from "mobx-react";
import { Document } from "../../../fields/Document";
import { Field, FieldWaiting, Opt } from "../../../fields/Field";
@@ -8,7 +8,7 @@ import { ListField } from "../../../fields/ListField";
import { BooleanField } from "../../../fields/BooleanField";
import { TextField } from "../../../fields/TextField";
import { ServerUtils } from "../../../server/ServerUtil";
-import { Utils } from "../../../Utils";
+import { Utils, emptyFunction } from "../../../Utils";
import { Documents } from "../../documents/Documents";
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager } from "../../util/DragManager";
@@ -62,12 +62,12 @@ export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs {
let Keys: { [name: string]: any } = {};
let Fields: { [name: string]: any } = {};
for (const key of keys) {
- let fn = () => { };
+ let fn = emptyFunction;
Object.defineProperty(fn, "name", { value: key + "Key" });
Keys[key] = fn;
}
for (const field of fields) {
- let fn = () => { };
+ let fn = emptyFunction;
Object.defineProperty(fn, "name", { value: field });
Fields[field] = fn;
}
@@ -105,7 +105,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
e.stopPropagation();
} else {
- if (this.active && !e.isDefaultPrevented()) {
+ if (this.active) {
e.stopPropagation();
document.removeEventListener("pointermove", this.onPointerMove);
document.addEventListener("pointermove", this.onPointerMove);
@@ -157,7 +157,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
dragData.moveDocument = this.props.moveDocument;
DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, {
handlers: {
- dragComplete: action(() => { })
+ dragComplete: action(emptyFunction)
},
hideSource: !dropAliasOfDraggedDoc
});
@@ -186,6 +186,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
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
) {
@@ -377,14 +378,26 @@ export class DocumentView extends React.Component<DocumentViewProps> {
SelectionManager.SelectDoc(this, ctrlPressed);
}
+ @computed get nativeWidth(): number { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
+ @computed get nativeHeight(): number { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
+ @computed
+ get contents() {
+ return (<DocumentContentsView
+ {...this.props}
+ isSelected={this.isSelected}
+ select={this.select}
+ layoutKey={KeyStore.Layout}
+ />);
+ }
+
render() {
if (!this.props.Document) {
return null;
}
var scaling = this.props.ContentScaling();
- var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
- var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
+ var nativeWidth = this.nativeWidth;
+ var nativeHeight = this.nativeHeight;
if (this.isMinimized()) {
return (
@@ -420,12 +433,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
onContextMenu={this.onContextMenu}
onPointerDown={this.onPointerDown}
>
- <DocumentContentsView
- {...this.props}
- isSelected={this.isSelected}
- select={this.select}
- layoutKey={KeyStore.Layout}
- />
+ {this.contents}
</div>
);
}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 07c5b332c..40b44aae5 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -19,7 +19,7 @@ import { ListField } from "../../../fields/ListField";
import { DocumentContentsView } from "./DocumentContentsView";
import { Transform } from "../../util/Transform";
import { KeyStore } from "../../../fields/KeyStore";
-import { returnFalse } from "../../../Utils";
+import { returnFalse, emptyFunction } from "../../../Utils";
//
@@ -85,9 +85,9 @@ export class FieldView extends React.Component<FieldViewProps> {
PanelHeight={() => 100}
isTopMost={true} //TODO Why is this top most?
selectOnLoad={false}
- focus={() => { }}
- isSelected={() => false}
- select={() => false}
+ focus={emptyFunction}
+ isSelected={returnFalse}
+ select={returnFalse}
layoutKey={KeyStore.Layout}
ContainingCollectionView={undefined}
parentActive={this.props.active}
@@ -111,7 +111,7 @@ export class FieldView extends React.Component<FieldViewProps> {
}
else {
return <p> {"Waiting for server..."} </p>;
- }
+ }
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 32da2632e..d2ba52cf9 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -22,6 +22,7 @@
overflow-x: hidden;
color: initial;
height: 100%;
+ pointer-events: all;
}
.menuicon {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index e872ea48b..41cb34416 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -5,7 +5,6 @@ import { keymap } from "prosemirror-keymap";
import { EditorState, Plugin, Transaction } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { FieldWaiting, Opt } from "../../../fields/Field";
-import { KeyStore } from "../../../fields/KeyStore";
import { RichTextField } from "../../../fields/RichTextField";
import { inpRules } from "../../util/RichTextRules";
import { Schema } from "prosemirror-model";
@@ -37,7 +36,12 @@ const { menuBar } = require("prosemirror-menu");
// specified Key and assigns it to an HTML input node. When changes are made to this node,
// this will edit the document and assign the new value to that field.
//]
-export class FormattedTextBox extends React.Component<FieldViewProps> {
+
+export interface FormattedTextBoxOverlay {
+ isOverlay?: boolean;
+}
+
+export class FormattedTextBox extends React.Component<(FieldViewProps & FormattedTextBoxOverlay)> {
public static LayoutString(fieldStr: string = "DataKey") {
return FieldView.LayoutString(FormattedTextBox, fieldStr);
}
@@ -45,6 +49,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
private _editorView: Opt<EditorView>;
private _reactionDisposer: Opt<IReactionDisposer>;
private _inputReactionDisposer: Opt<IReactionDisposer>;
+ private _proxyReactionDisposer: Opt<IReactionDisposer>;
constructor(props: FieldViewProps) {
super(props);
@@ -57,8 +62,8 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
if (this._editorView) {
const state = this._editorView.state.apply(tx);
this._editorView.updateState(state);
- this.FieldDoc.SetDataOnPrototype(
- this.FieldKey,
+ this.props.Document.SetDataOnPrototype(
+ this.props.fieldKey,
JSON.stringify(state.toJSON()),
RichTextField
);
@@ -66,22 +71,28 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
}
}
- get FieldDoc() { return this.props.fieldKey === KeyStore.Archives ? Main.Instance._textDoc! : this.props.Document; }
- get FieldKey() { return this.props.fieldKey === KeyStore.Archives ? KeyStore.Data : this.props.fieldKey; }
-
componentDidMount() {
const config = {
schema,
inpRules, //these currently don't do anything, but could eventually be helpful
- plugins: [
+ plugins: this.props.isOverlay ? [
history(),
keymap(buildKeymap(schema)),
keymap(baseKeymap),
- this.tooltipMenuPlugin()
- ]
+ this.tooltipMenuPlugin(),
+ new Plugin({
+ props: {
+ attributes: { class: "ProseMirror-example-setup-style" }
+ }
+ })
+ ] : [
+ history(),
+ keymap({ "Mod-z": undo, "Mod-y": redo }),
+ keymap(baseKeymap),
+ ]
};
- if (this.props.fieldKey === KeyStore.Archives) {
+ if (this.props.isOverlay) {
this._inputReactionDisposer = reaction(() => Main.Instance._textDoc && Main.Instance._textDoc.Id,
() => {
if (this._editorView) {
@@ -91,11 +102,14 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
this.setupEditor(config);
}
);
+ } else {
+ this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
+ () => this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform()));
}
this._reactionDisposer = reaction(
() => {
- const field = this.FieldDoc.GetT(this.FieldKey, RichTextField);
+ const field = this.props.Document ? this.props.Document.GetT(this.props.fieldKey, RichTextField) : undefined;
return field && field !== FieldWaiting ? field.Data : undefined;
},
field => {
@@ -110,9 +124,8 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
}
private setupEditor(config: any) {
-
let state: EditorState;
- let field = this.FieldDoc.GetT(this.FieldKey, RichTextField);
+ let field = this.props.Document ? this.props.Document.GetT(this.props.fieldKey, RichTextField) : undefined;
if (field && field !== FieldWaiting && field.Data) {
state = EditorState.fromJSON(config, JSON.parse(field.Data));
} else {
@@ -141,6 +154,9 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
if (this._inputReactionDisposer) {
this._inputReactionDisposer();
}
+ if (this._proxyReactionDisposer) {
+ this._proxyReactionDisposer();
+ }
}
shouldComponentUpdate() {
@@ -154,23 +170,30 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
// doc.SetData(fieldKey, e.target.value, RichTextField);
}
onPointerDown = (e: React.PointerEvent): void => {
- if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
+ console.log("pointer down");
+ if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
+ console.log("first");
e.stopPropagation();
}
+ if (e.button === 2) {
+ console.log("second");
+ e.preventDefault();
+ }
}
onPointerUp = (e: React.PointerEvent): void => {
+ console.log("pointer up");
if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
e.stopPropagation();
}
- if (this.props.fieldKey !== KeyStore.Archives) {
- e.preventDefault();
- Main.Instance.SetTextDoc(this.props.Document, this._ref.current!);
- }
}
onFocused = (e: React.FocusEvent): void => {
- if (this.props.fieldKey !== KeyStore.Archives) {
- Main.Instance.SetTextDoc(this.props.Document, this._ref.current!);
+ if (!this.props.isOverlay) {
+ Main.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform());
+ } else {
+ if (this._ref.current) {
+ this._ref.current.scrollTop = Main.Instance._textScroll;
+ }
}
}
@@ -201,16 +224,16 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
e.stopPropagation();
}
- tooltipMenuPlugin(): Plugin<any, any> {
+ tooltipMenuPlugin() {
+ let myprops = this.props;
return new Plugin({
view(_editorView) {
- return new TooltipTextMenu(_editorView);
+ return new TooltipTextMenu(_editorView, myprops);
}
});
}
onKeyPress(e: React.KeyboardEvent) {
- console.log(e.keyCode)
- // e.stopPropagation();
+ e.stopPropagation();
// stop propagation doesn't seem to stop propagation of native keyboard events.
// so we set a flag on the native event that marks that the event's been handled.
// (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
@@ -218,9 +241,11 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
render() {
return (
<div
- className="formattedTextBox-cont"
+ className={`formattedTextBox-cont`}
+ style={{ overflow: "visible" }}
onKeyDown={this.onKeyPress}
onKeyPress={this.onKeyPress}
+ onFocus={this.onFocused}
onPointerUp={this.onPointerUp}
onPointerDown={this.onPointerDown}
onContextMenu={this.specificContextMenu}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 314af64c9..9d7c2bc56 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -66,7 +66,7 @@ export class VideoBox extends React.Component<FieldViewProps> {
<Measure onResize={this.setScaling}>
{({ measureRef }) =>
<div style={{ width: "100%", height: "auto" }} ref={measureRef}>
- <video className="videobox-cont" onClick={() => { }} ref={this.setVideoRef}>
+ <video className="videobox-cont" ref={this.setVideoRef}>
<source src={path} type="video/mp4" />
Not supported.
</video>
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 425408273..da2d7268f 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -1,5 +1,4 @@
import { Key } from "./Key";
-import { KeyTransfer } from "../server/Message";
export namespace KeyStore {
export const Prototype = new Key("Prototype");
diff --git a/tsconfig.json b/tsconfig.json
index 41db1d0a7..0d4d77002 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,23 +1,23 @@
{
- "compilerOptions": {
- "target": "es5",
- "removeComments": true,
- "experimentalDecorators": true,
- "strict": true,
- "jsx": "react",
- "sourceMap": true,
- "outDir": "dist",
- "lib": [
- "dom",
- "es2015"
- ],
- },
- // "exclude": [
- // "node_modules",
- // "static"
- // ],
- "typeRoots": [
- "./node_modules/@types",
- "./src/typings"
- ]
+ "compilerOptions": {
+ "target": "es5",
+ "removeComments": true,
+ "experimentalDecorators": true,
+ "strict": true,
+ "jsx": "react",
+ "sourceMap": true,
+ "outDir": "dist",
+ "lib": [
+ "dom",
+ "es2015"
+ ],
+ },
+ // "exclude": [
+ // "node_modules",
+ // "static"
+ // ],
+ "typeRoots": [
+ "./node_modules/@types",
+ "./src/typings"
+ ]
} \ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
index 50079255f..574401807 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -3,95 +3,95 @@ var webpack = require('webpack');
const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = {
- mode: 'development',
- entry: {
- bundle: ["./src/client/views/Main.tsx", 'webpack-hot-middleware/client?reload=true'],
- viewer: ["./src/debug/Viewer.tsx", 'webpack-hot-middleware/client?reload=true'],
- test: ["./src/debug/Test.tsx", 'webpack-hot-middleware/client?reload=true'],
- inkControls: ["./src/mobile/InkControls.tsx", 'webpack-hot-middleware/client?reload=true'],
- imageUpload: ["./src/mobile/ImageUpload.tsx", 'webpack-hot-middleware/client?reload=true'],
- },
- devtool: "source-map",
- node: {
- fs: 'empty',
- module: 'empty',
- dns: 'mock',
- tls: 'mock',
- net: 'mock'
- },
- output: {
- filename: "[name].js",
- path: path.resolve(__dirname, "build"),
- publicPath: "/"
- },
- resolve: {
- extensions: ['.js', '.ts', '.tsx']
- },
- module: {
- rules: [
- {
- test: [/\.tsx?$/, /\.ts?$/,],
- enforce: 'pre',
- use: [
- {
- loader: "tslint-loader",
- }
- ]
- }, {
- test: [/\.tsx?$/, /\.ts?$/,],
- loader: "awesome-typescript-loader",
- include: path.join(__dirname, 'src')
- },
- {
- test: /\.scss|css$/,
- use: [
- {
- loader: "style-loader"
- },
- {
- loader: "css-loader"
- },
- {
- loader: "sass-loader"
- }
- ]
- },
- {
- test: /\.(jpg|png|pdf)$/,
- use: [
- {
- loader: 'file-loader'
- }
- ]
- },
- {
- test: /\.(png|jpg|gif)$/i,
- use: [
- {
- loader: 'url-loader',
- options: {
- limit: 8192
- }
- }
- ]
- }]
- },
- plugins: [
- new CopyWebpackPlugin([{ from: "deploy", to: path.join(__dirname, "build") }]),
- new webpack.optimize.OccurrenceOrderPlugin(),
- new webpack.HotModuleReplacementPlugin(),
- new webpack.NoEmitOnErrorsPlugin()
- ],
- devServer: {
- compress: false,
- host: "localhost",
- contentBase: path.join(__dirname, 'deploy'),
- port: 4321,
- hot: true,
- https: false,
- overlay: {
- warnings: true,
- errors: true
+ mode: 'development',
+ entry: {
+ bundle: ["./src/client/views/Main.tsx", 'webpack-hot-middleware/client?reload=true'],
+ viewer: ["./src/debug/Viewer.tsx", 'webpack-hot-middleware/client?reload=true'],
+ test: ["./src/debug/Test.tsx", 'webpack-hot-middleware/client?reload=true'],
+ inkControls: ["./src/mobile/InkControls.tsx", 'webpack-hot-middleware/client?reload=true'],
+ imageUpload: ["./src/mobile/ImageUpload.tsx", 'webpack-hot-middleware/client?reload=true'],
+ },
+ devtool: "source-map",
+ node: {
+ fs: 'empty',
+ module: 'empty',
+ dns: 'mock',
+ tls: 'mock',
+ net: 'mock'
+ },
+ output: {
+ filename: "[name].js",
+ path: path.resolve(__dirname, "build"),
+ publicPath: "/"
+ },
+ resolve: {
+ extensions: ['.js', '.ts', '.tsx']
+ },
+ module: {
+ rules: [
+ {
+ test: [/\.tsx?$/, /\.ts?$/,],
+ enforce: 'pre',
+ use: [
+ {
+ loader: "tslint-loader",
+ }
+ ]
+ }, {
+ test: [/\.tsx?$/, /\.ts?$/,],
+ loader: "awesome-typescript-loader",
+ include: path.join(__dirname, 'src')
+ },
+ {
+ test: /\.scss|css$/,
+ use: [
+ {
+ loader: "style-loader"
+ },
+ {
+ loader: "css-loader"
+ },
+ {
+ loader: "sass-loader"
+ }
+ ]
+ },
+ {
+ test: /\.(jpg|png|pdf)$/,
+ use: [
+ {
+ loader: 'file-loader'
+ }
+ ]
+ },
+ {
+ test: /\.(png|jpg|gif)$/i,
+ use: [
+ {
+ loader: 'url-loader',
+ options: {
+ limit: 8192
+ }
+ }
+ ]
+ }]
+ },
+ plugins: [
+ new CopyWebpackPlugin([{ from: "deploy", to: path.join(__dirname, "build") }]),
+ new webpack.optimize.OccurrenceOrderPlugin(),
+ new webpack.HotModuleReplacementPlugin(),
+ new webpack.NoEmitOnErrorsPlugin()
+ ],
+ devServer: {
+ compress: false,
+ host: "localhost",
+ contentBase: path.join(__dirname, 'deploy'),
+ port: 4321,
+ hot: true,
+ https: false,
+ overlay: {
+ warnings: true,
+ errors: true
+ }
}
- }
}; \ No newline at end of file