aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/Server.ts157
-rw-r--r--src/client/SocketStub.ts22
-rw-r--r--src/client/documents/Documents.ts22
-rw-r--r--src/client/northstar/model/ModelHelpers.ts10
-rw-r--r--src/client/northstar/operations/HistogramOperation.ts4
-rw-r--r--src/client/util/Scripting.ts10
-rw-r--r--src/client/util/type_decls.d10
-rw-r--r--src/client/views/EditableView.tsx11
-rw-r--r--src/client/views/Main.tsx170
-rw-r--r--src/client/views/collections/CollectionFreeFormView.tsx7
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx82
-rw-r--r--src/client/views/collections/CollectionView.tsx5
-rw-r--r--src/client/views/collections/CollectionViewBase.tsx41
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx7
-rw-r--r--src/client/views/nodes/DocumentView.tsx11
-rw-r--r--src/client/views/nodes/FieldView.tsx18
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx2
-rw-r--r--src/client/views/nodes/HistogramBox.tsx6
18 files changed, 376 insertions, 219 deletions
diff --git a/src/client/Server.ts b/src/client/Server.ts
index 7d882c76d..feafe9eb4 100644
--- a/src/client/Server.ts
+++ b/src/client/Server.ts
@@ -2,7 +2,7 @@ import { Key } from "../fields/Key"
import { ObservableMap, action, reaction } from "mobx";
import { Field, FieldWaiting, FIELD_WAITING, Opt, FieldId } from "../fields/Field"
import { Document } from "../fields/Document"
-import { SocketStub } from "./SocketStub";
+import { SocketStub, FieldMap } from "./SocketStub";
import * as OpenSocket from 'socket.io-client';
import { Utils } from "./../Utils";
import { MessageStore, Types } from "./../server/Message";
@@ -15,74 +15,102 @@ export class Server {
// Retrieves the cached value of the field and sends a request to the server for the real value (if it's not cached).
// Call this is from within a reaction and test whether the return value is FieldWaiting.
- // 'hackTimeout' is here temporarily for simplicity when debugging things.
- public static GetField(fieldid: FieldId, callback: (field: Opt<Field>) => void): Opt<Field> | FIELD_WAITING {
- let cached = this.ClientFieldsCached.get(fieldid);
- if (!cached) {
- this.ClientFieldsCached.set(fieldid, FieldWaiting);
- SocketStub.SEND_FIELD_REQUEST(fieldid, action((field: Field | undefined) => {
- let cached = this.ClientFieldsCached.get(fieldid);
- if (cached != FieldWaiting)
- callback(cached);
- else {
- if (field) {
- this.ClientFieldsCached.set(fieldid, field);
- } else {
- this.ClientFieldsCached.delete(fieldid)
+ public static GetField(fieldid: FieldId): Promise<Opt<Field>>;
+ public static GetField(fieldid: FieldId, callback: (field: Opt<Field>) => void): void;
+ public static GetField(fieldid: FieldId, callback?: (field: Opt<Field>) => void): Promise<Opt<Field>> | void {
+ let fn = (cb: (field: Opt<Field>) => void) => {
+
+ let cached = this.ClientFieldsCached.get(fieldid);
+ if (!cached) {
+ this.ClientFieldsCached.set(fieldid, FieldWaiting);
+ SocketStub.SEND_FIELD_REQUEST(fieldid, action((field: Field | undefined) => {
+ let cached = this.ClientFieldsCached.get(fieldid);
+ if (cached != FieldWaiting)
+ cb(cached);
+ else {
+ if (field) {
+ this.ClientFieldsCached.set(fieldid, field);
+ } else {
+ this.ClientFieldsCached.delete(fieldid)
+ }
+ cb(field)
}
- callback(field)
- }
- }));
- } else if (cached != FieldWaiting) {
- setTimeout(() => callback(cached as Field), 0);
+ }));
+ } else if (cached != FieldWaiting) {
+ setTimeout(() => cb(cached as Field), 0);
+ } else {
+ reaction(() => {
+ return this.ClientFieldsCached.get(fieldid);
+ }, (field, reaction) => {
+ if (field !== "<Waiting>") {
+ reaction.dispose()
+ cb(field)
+ }
+ })
+ }
+ }
+ if (callback) {
+ fn(callback);
} else {
- reaction(() => {
- return this.ClientFieldsCached.get(fieldid);
- }, (field, reaction) => {
- if (field !== FieldWaiting) {
- reaction.dispose()
- callback(field)
- }
- })
+ return new Promise(res => fn(res));
}
- return cached;
}
- public static GetFields(fieldIds: FieldId[], callback: (fields: { [id: string]: Field }) => any) {
- let neededFieldIds: FieldId[] = [];
- let waitingFieldIds: FieldId[] = [];
- let existingFields: { [id: string]: Field } = {};
- for (let id of fieldIds) {
- let field = this.ClientFieldsCached.get(id);
- if (!field) {
- neededFieldIds.push(id);
- this.ClientFieldsCached.set(id, FieldWaiting);
- } else if (field === FieldWaiting) {
- waitingFieldIds.push(id);
- } else {
- existingFields[id] = field;
- }
- }
- SocketStub.SEND_FIELDS_REQUEST(neededFieldIds, (fields) => {
- for (let key in fields) {
- let field = fields[key];
- if (!(this.ClientFieldsCached.get(field.Id) instanceof Field)) {
- this.ClientFieldsCached.set(field.Id, field)
+ 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 neededFieldIds: FieldId[] = [];
+ let waitingFieldIds: FieldId[] = [];
+ let existingFields: { [id: string]: Field } = {};
+ for (let id of fieldIds) {
+ let field = this.ClientFieldsCached.get(id);
+ if (!field) {
+ neededFieldIds.push(id);
+ this.ClientFieldsCached.set(id, FieldWaiting);
+ } else if (field === FieldWaiting) {
+ waitingFieldIds.push(id);
+ } else {
+ existingFields[id] = field;
}
}
- reaction(() => {
- return waitingFieldIds.map(this.ClientFieldsCached.get);
- }, (cachedFields, reaction) => {
- if (!cachedFields.some(field => !field || field === FieldWaiting)) {
- reaction.dispose();
- for (let field of cachedFields) {
- let realField = field as Field;
- existingFields[realField.Id] = realField;
+ SocketStub.SEND_FIELDS_REQUEST(neededFieldIds, (fields) => {
+ for (let id of neededFieldIds) {
+ let field = fields[id];
+ if (field) {
+ if (!(this.ClientFieldsCached.get(field.Id) instanceof Field)) {
+ this.ClientFieldsCached.set(field.Id, field)
+ } else {
+ throw new Error("we shouldn't be trying to replace things that are already in the cache")
+ }
+ } else {
+ if (this.ClientFieldsCached.get(id) === FieldWaiting) {
+ this.ClientFieldsCached.delete(id);
+ } else {
+ throw new Error("we shouldn't be trying to replace things that are already in the cache")
+ }
}
- callback({ ...fields, ...existingFields })
}
- }, { fireImmediately: true })
- });
+ reaction(() => {
+ return waitingFieldIds.map(id => this.ClientFieldsCached.get(id));
+ }, (cachedFields, reaction) => {
+ if (!cachedFields.some(field => !field || field === FieldWaiting)) {
+ reaction.dispose();
+ for (let field of cachedFields) {
+ let realField = field as Field;
+ existingFields[realField.Id] = realField;
+ }
+ cb({ ...fields, ...existingFields })
+ }
+ }, { fireImmediately: true })
+ });
+ };
+ if (callback) {
+ fn(callback);
+ } else {
+ return new Promise(res => fn(res));
+ }
}
public static GetDocumentField(doc: Document, key: Key, callback?: (field: Field) => void) {
@@ -138,12 +166,17 @@ export class Server {
if (Server.ClientFieldsCached.has(field._id)) {
var f = Server.ClientFieldsCached.get(field._id);
if (f && f != FieldWaiting) {
+ // console.log("Applying : " + field._id);
f.UpdateFromServer(field.data);
f.init(() => { });
+ } else {
+ // console.log("Not applying wa : " + field._id);
}
+ } else {
+ // console.log("Not applying mi : " + field._id);
}
}
}
-Server.Socket.on(MessageStore.Foo.Message, Server.connected);
-Server.Socket.on(MessageStore.SetField.Message, Server.updateField);
+Utils.AddServerHandler(Server.Socket, MessageStore.Foo, Server.connected);
+Utils.AddServerHandler(Server.Socket, MessageStore.SetField, Server.updateField);
diff --git a/src/client/SocketStub.ts b/src/client/SocketStub.ts
index a0b89b7c9..5045037c5 100644
--- a/src/client/SocketStub.ts
+++ b/src/client/SocketStub.ts
@@ -7,6 +7,11 @@ import { Utils } from "../Utils";
import { Server } from "./Server";
import { ServerUtils } from "../server/ServerUtil";
+
+export interface FieldMap {
+ [id: string]: Opt<Field>;
+}
+
//TODO tfs: I think it might be cleaner to not have SocketStub deal with turning what the server gives it into Fields (in other words not call ServerUtils.FromJson), and leave that for the Server class.
export class SocketStub {
@@ -35,19 +40,26 @@ export class SocketStub {
Utils.Emit(Server.Socket, MessageStore.AddDocument, new DocumentTransfer(document.ToJson()))
}
- public static SEND_FIELD_REQUEST(fieldid: FieldId, callback: (field: Opt<Field>) => void) {
- if (fieldid) {
+ public static SEND_FIELD_REQUEST(fieldid: FieldId): Promise<Opt<Field>>;
+ public static SEND_FIELD_REQUEST(fieldid: FieldId, callback: (field: Opt<Field>) => void): void;
+ public static SEND_FIELD_REQUEST(fieldid: FieldId, callback?: (field: Opt<Field>) => void): Promise<Opt<Field>> | void {
+ let fn = function (cb: (field: Opt<Field>) => void) {
Utils.EmitCallback(Server.Socket, MessageStore.GetField, fieldid, (field: any) => {
if (field) {
- ServerUtils.FromJson(field).init(callback);
+ ServerUtils.FromJson(field).init(cb);
} else {
- callback(undefined);
+ cb(undefined);
}
})
}
+ if (callback) {
+ fn(callback);
+ } else {
+ return new Promise(res => fn(res))
+ }
}
- public static SEND_FIELDS_REQUEST(fieldIds: FieldId[], callback: (fields: { [key: string]: Field }) => any) {
+ public static SEND_FIELDS_REQUEST(fieldIds: FieldId[], callback: (fields: FieldMap) => any) {
Utils.EmitCallback(Server.Socket, MessageStore.GetFields, fieldIds, (fields: any[]) => {
let fieldMap: any = {};
for (let field of fields) {
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index cf6d7d503..a2444db06 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, FieldWaiting } from "../../fields/Field";
import { HtmlField } from "../../fields/HtmlField";
import { ImageField } from "../../fields/ImageField";
import { InkField, StrokeData } from "../../fields/InkField";
@@ -23,6 +23,7 @@ import { PDFBox } from "../views/nodes/PDFBox";
import { VideoBox } from "../views/nodes/VideoBox";
import { WebBox } from "../views/nodes/WebBox";
import { HistogramBox } from "../views/nodes/HistogramBox";
+import { FieldView } from "../views/nodes/FieldView";
export interface DocumentOptions {
x?: number;
@@ -63,15 +64,14 @@ export namespace Documents {
const videoProtoId = "videoProto"
const audioProtoId = "audioProto";
- export function initProtos(callback: () => void) {
- Server.GetFields([collProtoId, textProtoId, imageProtoId], (fields) => {
+ export function initProtos(): Promise<void> {
+ return Server.GetFields([textProtoId, histoProtoId, collProtoId, imageProtoId, webProtoId, kvpProtoId]).then(fields => {
textProto = fields[textProtoId] as Document;
histoProto = fields[histoProtoId] as Document;
collProto = fields[collProtoId] as Document;
imageProto = fields[imageProtoId] as Document;
webProto = fields[webProtoId] as Document;
kvpProto = fields[kvpProtoId] as Document;
- callback();
});
}
function assignOptions(doc: Document, options: DocumentOptions): Document {
@@ -113,7 +113,7 @@ export namespace Documents {
function GetImagePrototype(): Document {
if (!imageProto) {
imageProto = setupPrototypeOptions(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("AnnotationsKey"),
- { x: 0, y: 0, nativeWidth: 300, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations] });
+ { x: 0, y: 0, nativeWidth: 300, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] });
imageProto.SetText(KeyStore.BackgroundLayout, ImageBox.LayoutString());
}
return imageProto;
@@ -156,7 +156,7 @@ export namespace Documents {
function GetVideoPrototype(): Document {
if (!videoProto) {
videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionVideoView.LayoutString("AnnotationsKey"),
- { x: 0, y: 0, nativeWidth: 600, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations] });
+ { x: 0, y: 0, nativeWidth: 600, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] });
videoProto.SetNumber(KeyStore.CurPage, 0);
videoProto.SetText(KeyStore.BackgroundLayout, VideoBox.LayoutString());
}
@@ -219,6 +219,14 @@ export namespace Documents {
return assignToDelegate(SetInstanceOptions(GetCollectionPrototype(), { ...options, viewType: CollectionViewType.Docking }, [config, TextField], id), options)
}
+ export function CaptionDocument(doc: Document) {
+ const captionDoc = doc.CreateAlias();
+ captionDoc.SetText(KeyStore.OverlayLayout, FixedCaption());
+ captionDoc.SetNumber(KeyStore.Width, doc.GetNumber(KeyStore.Width, 0));
+ captionDoc.SetNumber(KeyStore.Height, doc.GetNumber(KeyStore.Height, 0));
+ return captionDoc;
+ }
+
// example of custom display string for an image that shows a caption.
function EmbeddedCaption() {
return `<div style="height:100%">
@@ -229,7 +237,7 @@ export namespace Documents {
+ FormattedTextBox.LayoutString("CaptionKey") +
`</div>
</div>` };
- function FixedCaption(fieldName: string = "Caption") {
+ 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") +
diff --git a/src/client/northstar/model/ModelHelpers.ts b/src/client/northstar/model/ModelHelpers.ts
index e1241b3ef..914e03255 100644
--- a/src/client/northstar/model/ModelHelpers.ts
+++ b/src/client/northstar/model/ModelHelpers.ts
@@ -9,7 +9,7 @@ import { AlphabeticVisualBinRange } from "./binRanges/AlphabeticVisualBinRange";
import { NominalVisualBinRange } from "./binRanges/NominalVisualBinRange";
import { VisualBinRangeHelper } from "./binRanges/VisualBinRangeHelper";
import { AttributeTransformationModel } from "../core/attribute/AttributeTransformationModel";
-import { Main } from "../../views/Main";
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
export class ModelHelpers {
@@ -39,19 +39,19 @@ export class ModelHelpers {
if (atm.AggregateFunction === AggregateFunction.Avg) {
var avg = new AverageAggregateParameters();
avg.attributeParameters = ModelHelpers.GetAttributeParameters(atm.AttributeModel);
- avg.distinctAttributeParameters = Main.Instance.ActiveSchema!.distinctAttributeParameters;
+ avg.distinctAttributeParameters = CurrentUserUtils.ActiveSchema!.distinctAttributeParameters;
aggParam = avg;
}
else if (atm.AggregateFunction === AggregateFunction.Count) {
var cnt = new CountAggregateParameters();
cnt.attributeParameters = ModelHelpers.GetAttributeParameters(atm.AttributeModel);
- cnt.distinctAttributeParameters = Main.Instance.ActiveSchema!.distinctAttributeParameters;
+ cnt.distinctAttributeParameters = CurrentUserUtils.ActiveSchema!.distinctAttributeParameters;
aggParam = cnt;
}
else if (atm.AggregateFunction === AggregateFunction.Sum) {
var sum = new SumAggregateParameters();
sum.attributeParameters = ModelHelpers.GetAttributeParameters(atm.AttributeModel);
- sum.distinctAttributeParameters = Main.Instance.ActiveSchema!.distinctAttributeParameters;
+ sum.distinctAttributeParameters = CurrentUserUtils.ActiveSchema!.distinctAttributeParameters;
aggParam = sum;
}
return aggParam;
@@ -66,7 +66,7 @@ export class ModelHelpers {
var margin = new MarginAggregateParameters();
margin.attributeParameters = ModelHelpers.GetAttributeParameters(agg.AttributeModel);
- margin.distinctAttributeParameters = Main.Instance.ActiveSchema!.distinctAttributeParameters;
+ margin.distinctAttributeParameters = CurrentUserUtils.ActiveSchema!.distinctAttributeParameters;
margin.aggregateFunction = agg.AggregateFunction;
aggregateParameters.push(margin);
}
diff --git a/src/client/northstar/operations/HistogramOperation.ts b/src/client/northstar/operations/HistogramOperation.ts
index 5ee1c0795..8367cc725 100644
--- a/src/client/northstar/operations/HistogramOperation.ts
+++ b/src/client/northstar/operations/HistogramOperation.ts
@@ -5,8 +5,8 @@ import { CalculatedAttributeManager } from "../core/attribute/CalculatedAttribut
import { ModelHelpers } from "../model/ModelHelpers";
import { SETTINGS_X_BINS, SETTINGS_Y_BINS, SETTINGS_SAMPLE_SIZE } from "../model/binRanges/VisualBinRangeHelper";
import { AttributeTransformationModel } from "../core/attribute/AttributeTransformationModel";
-import { Main } from "../../views/Main";
import { BaseOperation } from "./BaseOperation";
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
import { FilterModel } from "../core/filter/FilterModel";
@@ -86,7 +86,7 @@ export class HistogramOperation extends BaseOperation {
let [perBinAggregateParameters, globalAggregateParameters] = this.GetAggregateParameters(this.X, this.Y, this.V);
return new HistogramOperationParameters({
enableBrushComputation: true,
- adapterName: Main.Instance.ActiveSchema!.displayName,
+ adapterName: CurrentUserUtils.ActiveSchema!.displayName,
filter: this.FilterString,
brushes: this.BrushString,
binningParameters: [ModelHelpers.GetBinningParameters(this.X, SETTINGS_X_BINS, this.QRange ? this.QRange.minValue : undefined, this.QRange ? this.QRange.maxValue : undefined),
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 46bd1a206..4e97b9401 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -15,6 +15,8 @@ import { ListField } from "../../fields/ListField";
// @ts-ignore
import * as typescriptlib from '!!raw-loader!./type_decls.d'
+import { Documents } from "../documents/Documents";
+import { Key } from "../../fields/Key";
export interface ExecutableScript {
@@ -28,9 +30,9 @@ function Compile(script: string | undefined, diagnostics: Opt<any[]>, scope: { [
let func: () => Opt<Field>;
if (compiled && script) {
- let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField];
- let paramNames = ["KeyStore", ...fieldTypes.map(fn => fn.name)];
- let params: any[] = [KeyStore, ...fieldTypes]
+ let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField, Key];
+ let paramNames = ["KeyStore", "Documents", ...fieldTypes.map(fn => fn.name)];
+ let params: any[] = [KeyStore, Documents, ...fieldTypes]
for (let prop in scope) {
if (prop === "this") {
continue;
@@ -110,7 +112,7 @@ export function CompileScript(script: string, scope?: { [name: string]: any }, a
let host = new ScriptingCompilerHost;
let funcScript = `(function() {
${addReturn ? `return ${script};` : script}
- })()`
+ }).apply(this)`
host.writeFile("file.ts", funcScript);
host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
let program = ts.createProgram(["file.ts"], {}, host);
diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d
index 679f73f42..4f69053b1 100644
--- a/src/client/util/type_decls.d
+++ b/src/client/util/type_decls.d
@@ -174,6 +174,7 @@ declare class ListField<T> extends BasicField<T[]>{
Copy(): Field;
}
declare class Key extends Field {
+ constructor(name:string);
Name: string;
TrySetValue(value: any): boolean;
GetValue(): any;
@@ -213,3 +214,12 @@ declare class Document extends Field {
GetAllPrototypes(): Document[];
MakeDelegate(): Document;
}
+
+declare const KeyStore: {
+ [name: string]: Key;
+}
+
+// @ts-ignore
+declare const console: any;
+
+declare const Documents: any;
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 579d6e6ad..29bf6add7 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -16,6 +16,8 @@ export interface EditableProps {
* */
SetValue(value: string): boolean;
+ OnFillDown?(value: string): void;
+
/**
* The contents to render when not editing
*/
@@ -36,8 +38,13 @@ export class EditableView extends React.Component<EditableProps> {
@action
onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
- if (e.key == "Enter" && !e.ctrlKey) {
- if (this.props.SetValue(e.currentTarget.value)) {
+ if (e.key == "Enter") {
+ if (!e.ctrlKey) {
+ if (this.props.SetValue(e.currentTarget.value)) {
+ this.editing = false;
+ }
+ } else if (this.props.OnFillDown) {
+ this.props.OnFillDown(e.currentTarget.value);
this.editing = false;
}
} else if (e.key == "Escape") {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 53a5aba6e..6534cb4f7 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -8,6 +8,7 @@ import "./Main.scss";
import { MessageStore } from '../../server/Message';
import { Utils } from '../../Utils';
import * as request from 'request'
+import * as rp from 'request-promise'
import { Documents } from '../documents/Documents';
import { Server } from '../Server';
import { setupDrag } from '../util/DragManager';
@@ -22,7 +23,7 @@ import "./Main.scss";
import { observer } from 'mobx-react';
import { InkingControl } from './InkingControl';
import { RouteStore } from '../../server/RouteStore';
-import { library } from '@fortawesome/fontawesome-svg-core';
+import { library, IconName } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faFont } from '@fortawesome/free-solid-svg-icons';
import { faImage } from '@fortawesome/free-solid-svg-icons';
@@ -40,7 +41,7 @@ import Measure from 'react-measure';
import { DashUserModel } from '../../server/authentication/models/user_model';
import { ServerUtils } from '../../server/ServerUtil';
import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
-import { Field, Opt } from '../../fields/Field';
+import { Field, Opt, FieldWaiting } from '../../fields/Field';
import { ListField } from '../../fields/ListField';
import { Gateway, Settings } from '../northstar/manager/Gateway';
import { Catalog, Schema, Attribute, AttributeGroup } from '../northstar/model/idea/idea';
@@ -51,16 +52,26 @@ import '../northstar/utils/Extensions'
@observer
export class Main extends React.Component {
// dummy initializations keep the compiler happy
- @observable private mainContainer?: Document;
@observable private mainfreeform?: Document;
- @observable private userWorkspaces: Document[] = [];
@observable public pwidth: number = 0;
@observable public pheight: number = 0;
- @observable ActiveSchema: Schema | undefined;
private _northstarColumns: Document[] = [];
- public mainDocId: string | undefined;
- private currentUser?: DashUserModel;
+ @computed private get mainContainer(): Document | undefined {
+ let doc = this.userDocument.GetT(KeyStore.ActiveWorkspace, Document);
+ return doc == FieldWaiting ? undefined : doc;
+ }
+
+ private set mainContainer(doc: Document | undefined) {
+ if (doc) {
+ this.userDocument.Set(KeyStore.ActiveWorkspace, doc);
+ }
+ }
+
+ private get userDocument(): Document {
+ return CurrentUserUtils.UserDocument;
+ }
+
public static Instance: Main;
constructor(props: Readonly<{}>) {
@@ -70,9 +81,12 @@ export class Main extends React.Component {
configure({ enforceActions: "observed" });
if (window.location.pathname !== RouteStore.home) {
let pathname = window.location.pathname.split("/");
- this.mainDocId = pathname[pathname.length - 1];
+ if (pathname.length > 1 && pathname[pathname.length - 2] == 'doc') {
+ CurrentUserUtils.MainDocId = pathname[pathname.length - 1];
+ }
};
+ // this.initializeNorthstar();
let y = "";
y.ReplaceAll("a", "B");
@@ -92,7 +106,7 @@ export class Main extends React.Component {
library.add(faTree);
this.initEventListeners();
- Documents.initProtos(() => this.initAuthenticationRouters());
+ this.initAuthenticationRouters();
this.initializeNorthstar();
}
@@ -100,8 +114,8 @@ export class Main extends React.Component {
onHistory = () => {
if (window.location.pathname !== RouteStore.home) {
let pathname = window.location.pathname.split("/");
- this.mainDocId = pathname[pathname.length - 1];
- Server.GetField(this.mainDocId, action((field: Opt<Field>) => {
+ CurrentUserUtils.MainDocId = pathname[pathname.length - 1];
+ Server.GetField(CurrentUserUtils.MainDocId, action((field: Opt<Field>) => {
if (field instanceof Document) {
this.openWorkspace(field, true);
}
@@ -131,63 +145,50 @@ export class Main extends React.Component {
initAuthenticationRouters = () => {
// Load the user's active workspace, or create a new one if initial session after signup
- request.get(ServerUtils.prepend(RouteStore.getActiveWorkspace), (error, response, body) => {
- if (this.mainDocId || body) {
- Server.GetField(this.mainDocId || body, field => {
- if (field instanceof Document) {
- this.openWorkspace(field);
- this.populateWorkspaces();
- } else {
- this.createNewWorkspace(true, this.mainDocId);
- }
- });
- } else {
- this.createNewWorkspace(true, this.mainDocId);
- }
- });
- }
-
- @action
- createNewWorkspace = (init: boolean, id?: string): void => {
- let mainDoc = Documents.DockDocument(JSON.stringify({ content: [{ type: 'row', content: [] }] }), { title: `Main Container ${this.userWorkspaces.length + 1}` }, id);
- let newId = mainDoc.Id;
- request.post(ServerUtils.prepend(RouteStore.addWorkspace), {
- body: { target: newId },
- json: true
- }, () => { if (init) this.populateWorkspaces(); });
-
- // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
- setTimeout(() => {
- let freeformDoc = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" });
- var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] };
- mainDoc.SetText(KeyStore.Data, JSON.stringify(dockingLayout));
- mainDoc.Set(KeyStore.ActiveFrame, freeformDoc);
- this.openWorkspace(mainDoc);
- let pendingDocument = Documents.SchemaDocument([], { title: "New Mobile Uploads" })
- mainDoc.Set(KeyStore.OptionalRightCollection, pendingDocument);
- }, 0);
- this.userWorkspaces.push(mainDoc);
+ if (!CurrentUserUtils.MainDocId) {
+ this.userDocument.GetTAsync(KeyStore.ActiveWorkspace, Document).then(doc => {
+ if (doc) {
+ this.openWorkspace(doc);
+ } else {
+ this.createNewWorkspace();
+ }
+ })
+ } else {
+ Server.GetField(CurrentUserUtils.MainDocId).then(field => {
+ if (field instanceof Document) {
+ this.openWorkspace(field)
+ } else {
+ this.createNewWorkspace(CurrentUserUtils.MainDocId);
+ }
+ })
+ }
}
@action
- populateWorkspaces = () => {
- // retrieve all workspace documents from the server
- request.get(ServerUtils.prepend(RouteStore.getAllWorkspaces), (error, res, body) => {
- let ids = JSON.parse(body) as string[];
- Server.GetFields(ids, action((fields: { [id: string]: Field }) => this.userWorkspaces = ids.map(id => fields[id] as Document)));
- });
+ createNewWorkspace = (id?: string): void => {
+ this.userDocument.GetTAsync<ListField<Document>>(KeyStore.Workspaces, ListField).then(action((list: Opt<ListField<Document>>) => {
+ if (list) {
+ let freeformDoc = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" });
+ var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] };
+ let mainDoc = Documents.DockDocument(JSON.stringify(dockingLayout), { title: `Main Container ${list.Data.length + 1}` }, id);
+ list.Data.push(mainDoc);
+ // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
+ setTimeout(() => {
+ mainDoc.Set(KeyStore.ActiveFrame, freeformDoc);
+ this.openWorkspace(mainDoc);
+ let pendingDocument = Documents.SchemaDocument([], { title: "New Mobile Uploads" })
+ mainDoc.Set(KeyStore.OptionalRightCollection, pendingDocument);
+ }, 0);
+ }
+ }));
}
@action
openWorkspace = (doc: Document, fromHistory = false): void => {
- request.post(ServerUtils.prepend(RouteStore.setActiveWorkspace), {
- body: { target: doc.Id },
- json: true
- });
this.mainContainer = doc;
fromHistory || window.history.pushState(null, doc.Title, "/doc/" + doc.Id);
this.mainContainer.GetTAsync(KeyStore.ActiveFrame, Document, field => this.mainfreeform = field);
- this.mainContainer.GetTAsync(KeyStore.OptionalRightCollection, Document, col => {
+ this.userDocument.GetTAsync(KeyStore.OptionalRightCollection, Document).then(col => {
// if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized)
setTimeout(() => {
if (col) {
@@ -201,10 +202,15 @@ export class Main extends React.Component {
});
}
+ @observable
+ workspacesShown: boolean = false;
+
+ areWorkspacesShown = () => {
+ return this.workspacesShown;
+ }
+ @action
toggleWorkspaces = () => {
- if (WorkspacesMenu.Instance) {
- WorkspacesMenu.Instance.toggle()
- }
+ this.workspacesShown = !this.workspacesShown;
}
screenToLocalTransform = () => Transform.Identity
@@ -247,7 +253,7 @@ export class Main extends React.Component {
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" }))
- let btns = [
+ let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Document][] = [
[React.createRef<HTMLDivElement>(), "font", "Add Textbox", addTextNode],
[React.createRef<HTMLDivElement>(), "image", "Add Image", addImageNode],
[React.createRef<HTMLDivElement>(), "file-pdf", "Add PDF", addPDFNode],
@@ -268,9 +274,9 @@ export class Main extends React.Component {
<div id="add-options-content">
<ul id="add-options-list">
{btns.map(btn =>
- <li key={btn[1] as string} ><div ref={btn[0] as React.RefObject<HTMLDivElement>}>
- <button className="round-button add-button" title={btn[2] as string} onPointerDown={setupDrag(btn[0] as React.RefObject<HTMLDivElement>, btn[3] as any)} onClick={addClick(btn[3] as any)}>
- <FontAwesomeIcon icon={btn[1] as any} size="sm" />
+ <li key={btn[1]} ><div ref={btn[0]}>
+ <button className="round-button add-button" title={btn[2]} onPointerDown={setupDrag(btn[0], btn[3])} onClick={addClick(btn[3])}>
+ <FontAwesomeIcon icon={btn[1]} size="sm" />
</button>
</div></li>)}
</ul>
@@ -300,6 +306,12 @@ export class Main extends React.Component {
}
render() {
+ let workspaceMenu: any = null;
+ let workspaces = this.userDocument.GetT<ListField<Document>>(KeyStore.Workspaces, ListField);
+ if (workspaces && workspaces !== FieldWaiting) {
+ workspaceMenu = <WorkspacesMenu active={this.mainContainer} open={this.openWorkspace} new={this.createNewWorkspace} allWorkspaces={workspaces.Data}
+ isShown={this.areWorkspacesShown} toggle={this.toggleWorkspaces} />
+ }
return (
<div id="main-div">
<Measure onResize={(r: any) => runInAction(() => {
@@ -316,7 +328,7 @@ export class Main extends React.Component {
<ContextMenu />
{this.nodesMenu}
{this.miscButtons}
- <WorkspacesMenu active={this.mainContainer} open={this.openWorkspace} new={this.createNewWorkspace} allWorkspaces={this.userWorkspaces} />
+ {workspaceMenu}
<InkingControl />
</div>
);
@@ -326,8 +338,8 @@ export class Main extends React.Component {
@action SetNorthstarCatalog(ctlog: Catalog) {
if (ctlog && ctlog.schemas) {
- this.ActiveSchema = ArrayUtil.FirstOrDefault<Schema>(ctlog.schemas!, (s: Schema) => s.displayName === "mimic");
- this._northstarColumns = this.GetAllNorthstarColumnAttributes().map(a => Documents.HistogramDocument({ width: 200, height: 200, title: a.displayName! }));
+ CurrentUserUtils.ActiveSchema = ArrayUtil.FirstOrDefault<Schema>(ctlog.schemas!, (s: Schema) => s.displayName === "mimic");
+ this._northstarColumns = CurrentUserUtils.GetAllNorthstarColumnAttributes().map(a => Documents.HistogramDocument({ width: 200, height: 200, title: a.displayName! }));
}
}
async initializeNorthstar(): Promise<void> {
@@ -342,22 +354,10 @@ export class Main extends React.Component {
let cat = Gateway.Instance.ClearCatalog();
cat.then(async () => this.SetNorthstarCatalog(await Gateway.Instance.GetCatalog()));
}
- public GetAllNorthstarColumnAttributes() {
- if (!this.ActiveSchema || !this.ActiveSchema.rootAttributeGroup) {
- return [];
- }
- const recurs = (attrs: Attribute[], g: AttributeGroup) => {
- if (g.attributes) {
- attrs.push.apply(attrs, g.attributes);
- if (g.attributeGroups) {
- g.attributeGroups.forEach(ng => recurs(attrs, ng));
- }
- }
- };
- const allAttributes: Attribute[] = new Array<Attribute>();
- recurs(allAttributes, this.ActiveSchema.rootAttributeGroup);
- return allAttributes;
- }
}
-ReactDOM.render(<Main />, document.getElementById('root'));
+Documents.initProtos().then(() => {
+ return CurrentUserUtils.loadCurrentUser()
+}).then(() => {
+ ReactDOM.render(<Main />, document.getElementById('root'));
+});
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 8b9d178c9..53fe969fe 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -75,7 +75,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
@undoBatch
@action
- drop = (e: Event, de: DragManager.DropEvent): boolean => {
+ drop = (e: Event, de: DragManager.DropEvent) => {
if (super.drop(e, de)) {
if (de.data instanceof DragManager.DocumentDragData) {
let screenX = de.x - (de.data.xOffset as number || 0);
@@ -83,6 +83,10 @@ export class CollectionFreeFormView extends CollectionViewBase {
const [x, y] = this.getTransform().transformPoint(screenX, screenY);
de.data.droppedDocument.SetNumber(KeyStore.X, x);
de.data.droppedDocument.SetNumber(KeyStore.Y, y);
+ if (!de.data.droppedDocument.GetNumber(KeyStore.Width, 0)) {
+ de.data.droppedDocument.SetNumber(KeyStore.Width, 300);
+ de.data.droppedDocument.SetNumber(KeyStore.Height, 300);
+ }
this.bringToFront(de.data.droppedDocument);
}
return true;
@@ -262,6 +266,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
const lvalue = this.props.Document.GetT<ListField<Document>>(this.props.fieldKey, ListField);
if (lvalue && lvalue != FieldWaiting) {
return lvalue.Data.map(doc => {
+ if (!doc) return null;
var page = doc.GetNumber(KeyStore.Page, 0);
return (page != curPage && page != 0) ? (null) :
(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 696527049..0ff6c3b40 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1,6 +1,6 @@
import React = require("react")
import { library } from '@fortawesome/fontawesome-svg-core';
-import { faCog } from '@fortawesome/free-solid-svg-icons';
+import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, trace, untracked } from "mobx";
import { observer } from "mobx-react";
@@ -8,7 +8,7 @@ import Measure from "react-measure";
import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
import "react-table/react-table.css";
import { Document } from "../../../fields/Document";
-import { Field, Opt } from "../../../fields/Field";
+import { Field, Opt, FieldWaiting } from "../../../fields/Field";
import { Key } from "../../../fields/Key";
import { KeyStore } from "../../../fields/KeyStore";
import { ListField } from "../../../fields/ListField";
@@ -24,6 +24,7 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView";
import "./CollectionSchemaView.scss";
import { CollectionView, COLLECTION_BORDER_WIDTH } from "./CollectionView";
import { CollectionViewBase } from "./CollectionViewBase";
+import { TextField } from "../../../fields/TextField";
// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@@ -85,12 +86,31 @@ export class CollectionSchemaView extends CollectionViewBase {
)
let reference = React.createRef<HTMLDivElement>();
let onItemDown = setupDrag(reference, () => props.doc, (containingCollection: CollectionView) => this.props.removeDocument(props.doc));
+ let applyToDoc = (doc: Document, value: string) => {
+ let script = CompileScript(value, { this: doc }, true);
+ if (!script.compiled) {
+ return false;
+ }
+ let field = script();
+ if (field instanceof Field) {
+ doc.Set(props.fieldKey, field);
+ return true;
+ } else {
+ let dataField = ToField(field);
+ if (dataField) {
+ doc.Set(props.fieldKey, dataField);
+ return true;
+ }
+ }
+ return false;
+ }
return (
- <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "36px" }} key={props.doc.Id} ref={reference}>
+ <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "56px" }} key={props.doc.Id} ref={reference}>
<EditableView
display={"inline"}
contents={contents}
- height={36} GetValue={() => {
+ height={56}
+ GetValue={() => {
let field = props.doc.Get(props.fieldKey);
if (field && field instanceof Field) {
return field.ToScriptString();
@@ -98,22 +118,14 @@ export class CollectionSchemaView extends CollectionViewBase {
return field || "";
}}
SetValue={(value: string) => {
- let script = CompileScript(value);
- if (!script.compiled) {
- return false;
- }
- let field = script();
- if (field instanceof Field) {
- props.doc.Set(props.fieldKey, field);
- return true;
- } else {
- let dataField = ToField(field);
- if (dataField) {
- props.doc.Set(props.fieldKey, dataField);
- return true;
+ return applyToDoc(props.doc, value);
+ }}
+ OnFillDown={(value: string) => {
+ this.props.Document.GetTAsync<ListField<Document>>(this.props.fieldKey, ListField).then((val) => {
+ if (val) {
+ val.Data.forEach(doc => applyToDoc(doc, value));
}
- }
- return false;
+ })
}}>
</EditableView>
</div>
@@ -238,6 +250,19 @@ export class CollectionSchemaView extends CollectionViewBase {
}
}
+ @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();
@@ -248,18 +273,29 @@ export class CollectionSchemaView extends CollectionViewBase {
OptionsMenuDown = (e: React.PointerEvent) => {
this._optionsActivated++;
}
+
+ @observable previewScript: string = "this";
+ @action
+ onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this.previewScript = e.currentTarget.value;
+ }
+
render() {
library.add(faCog);
+ library.add(faPlus);
const columns = this.columns;
const children = this.props.Document.GetList<Document>(this.props.fieldKey, []);
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;
+
+ // 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}>
- <DocumentView Document={selected}
+ {doc instanceof Document ? <DocumentView Document={doc}
AddDocument={this.props.addDocument} RemoveDocument={this.props.removeDocument}
isTopMost={false}
SelectOnLoad={false}
@@ -269,7 +305,9 @@ export class CollectionSchemaView extends CollectionViewBase {
PanelHeight={this.getPanelHeight}
ContainingCollectionView={this.props.CollectionView}
focus={this.focusDocument}
- />
+ /> : null}
+ <input value={this.previewScript} onChange={this.onPreviewScriptChange}
+ style={{ position: 'absolute', bottom: '0px' }} />
</div>
}
</Measure>
@@ -291,6 +329,8 @@ export class CollectionSchemaView extends CollectionViewBase {
return (<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>
</div>
</div>
}>
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index be757e22e..a740865ad 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -13,8 +13,7 @@ import { CollectionSchemaView } from "./CollectionSchemaView";
import { CollectionViewProps } from "./CollectionViewBase";
import { CollectionTreeView } from "./CollectionTreeView";
import { Field, FieldId, FieldWaiting } from "../../../fields/Field";
-import { Main } from "../Main";
-import { dropPoint } from "prosemirror-transform";
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
export enum CollectionViewType {
Invalid,
@@ -125,7 +124,7 @@ export class CollectionView extends React.Component<CollectionViewProps> {
}
specificContextMenu = (e: React.MouseEvent): void => {
- if (!e.isPropagationStopped() && this.props.Document.Id != Main.Instance.mainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+ if (!e.isPropagationStopped() && this.props.Document.Id != CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
ContextMenu.Instance.addItem({ description: "Freeform", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform) })
ContextMenu.Instance.addItem({ description: "Schema", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema) })
ContextMenu.Instance.addItem({ description: "Treeview", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree) })
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 535a8a8d2..adaf810ea 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -14,10 +14,9 @@ import { RouteStore } from "../../../server/RouteStore";
import { TupleField } from "../../../fields/TupleField";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
import { NumberField } from "../../../fields/NumberField";
-import { DocumentManager } from "../../util/DocumentManager";
import request = require("request");
import { ServerUtils } from "../../../server/ServerUtil";
-import { addHiddenFinalProp } from "mobx/lib/internal";
+import { Server } from "../../Server";
export interface CollectionViewProps {
fieldKey: Key;
@@ -60,17 +59,20 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
let email = CurrentUserUtils.email;
if (id && email) {
let textInfo: [string, string] = [id, email];
- doc.GetOrCreateAsync<ListField<CursorEntry>>(KeyStore.Cursors, ListField, field => {
- let cursors = field.Data;
- if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.Data[0][0] === id)) > -1) {
- cursors[ind].Data[1] = position;
- } else {
- let entry = new TupleField<[string, string], [number, number]>([textInfo, position]);
- cursors.push(entry);
+ doc.GetTAsync(KeyStore.Prototype, Document).then(proto => {
+ if (!proto) {
+ return;
}
+ proto.GetOrCreateAsync<ListField<CursorEntry>>(KeyStore.Cursors, ListField, action((field: ListField<CursorEntry>) => {
+ let cursors = field.Data;
+ if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.Data[0][0] === id)) > -1) {
+ cursors[ind].Data[1] = position;
+ } else {
+ let entry = new TupleField<[string, string], [number, number]>([textInfo, position]);
+ cursors.push(entry);
+ }
+ }))
})
-
-
}
}
@@ -115,6 +117,21 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
ctor = Documents.PdfDocument;
}
if (type.indexOf("html") !== -1) {
+ if (path.includes('localhost')) {
+ let s = path.split('/');
+ let id = s[s.length - 1];
+ Server.GetField(id).then(field => {
+ if (field instanceof Document) {
+ let alias = field.CreateAlias();
+ alias.SetNumber(KeyStore.X, options.x || 0);
+ alias.SetNumber(KeyStore.Y, options.y || 0);
+ alias.SetNumber(KeyStore.Width, options.width || 300);
+ alias.SetNumber(KeyStore.Height, options.height || options.width || 300);
+ this.props.addDocument(alias, false);
+ }
+ })
+ return undefined;
+ }
ctor = Documents.WebDocument;
options = { height: options.width, ...options, };
}
@@ -134,7 +151,7 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
e.stopPropagation()
e.preventDefault()
- if (html && html.indexOf("<img") != 0) {
+ if (html && html.indexOf("<img") != 0 && !html.startsWith("<a")) {
console.log("not good");
let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 });
htmlDoc.SetText(KeyStore.DocumentText, text);
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 2f0459f88..12d14eb42 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -47,8 +47,13 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
}
render() {
+ let lkeys = this.props.Document.GetT(KeyStore.LayoutKeys, ListField);
+ if (!lkeys || lkeys === FieldWaiting) {
+ return <p>Error loading layout keys</p>;
+ }
return <JsxParser
- components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }} bindings={this.CreateBindings()}
+ components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
+ bindings={this.CreateBindings()}
jsx={this.layout}
showWarnings={true}
onError={(test: any) => { console.log(test) }}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 383341e02..3873a6f89 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -18,6 +18,7 @@ import { ContextMenu } from "../ContextMenu";
import { DocumentContentsView } from "./DocumentContentsView";
import "./DocumentView.scss";
import React = require("react");
+import { ServerUtils } from "../../../server/ServerUtil";
export interface DocumentViewProps {
@@ -284,6 +285,12 @@ export class DocumentView extends React.Component<DocumentViewProps> {
ContextMenu.Instance.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) })
ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) })
ContextMenu.Instance.addItem({
+ description: "Copy URL",
+ event: () => {
+ Utils.CopyText(ServerUtils.prepend("/doc/" + this.props.Document.Id));
+ }
+ });
+ ContextMenu.Instance.addItem({
description: "Copy ID",
event: () => {
Utils.CopyText(this.props.Document.Id);
@@ -314,10 +321,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
if (!this.props.Document) {
return (null);
}
- let lkeys = this.props.Document.GetT(KeyStore.LayoutKeys, ListField);
- if (!lkeys || lkeys === FieldWaiting) {
- return <p>Error loading layout keys</p>;
- }
var scaling = this.props.ContentScaling();
var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index f6343c631..4e83ec7b9 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -16,6 +16,9 @@ import { VideoBox } from "./VideoBox";
import { AudioBox } from "./AudioBox";
import { AudioField } from "../../../fields/AudioField";
import { ListField } from "../../../fields/ListField";
+import { DocumentContentsView } from "./DocumentContentsView";
+import { Transform } from "../../util/Transform";
+import { KeyStore } from "../../../fields/KeyStore";
//
@@ -65,7 +68,20 @@ export class FieldView extends React.Component<FieldViewProps> {
return <AudioBox {...this.props} />
}
else if (field instanceof Document) {
- return <div>{field.Title}</div>
+ return (<DocumentContentsView Document={field}
+ AddDocument={undefined}
+ RemoveDocument={undefined}
+ ScreenToLocalTransform={() => Transform.Identity}
+ ContentScaling={() => 1}
+ PanelWidth={() => 100}
+ PanelHeight={() => 100}
+ isTopMost={true}
+ SelectOnLoad={false}
+ focus={() => { }}
+ isSelected={() => false}
+ select={() => false}
+ layoutKey={KeyStore.Layout}
+ ContainingCollectionView={undefined} />)
}
else if (field instanceof ListField) {
return (<div>
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index ba9bd9566..30fa1342e 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -167,6 +167,8 @@ export class FormattedTextBox extends React.Component<FieldViewProps> {
onKeyPress={this.onKeyPress}
onPointerDown={this.onPointerDown}
onContextMenu={this.specificContextMenu}
+ // tfs: do we need this event handler
+ onKeyDown={this.onKeyPress}
onWheel={this.onPointerWheel}
ref={this._ref} />)
}
diff --git a/src/client/views/nodes/HistogramBox.tsx b/src/client/views/nodes/HistogramBox.tsx
index 980719a21..1508b201b 100644
--- a/src/client/views/nodes/HistogramBox.tsx
+++ b/src/client/views/nodes/HistogramBox.tsx
@@ -3,13 +3,12 @@ import { action, computed, observable, reaction } from "mobx";
import { observer } from "mobx-react";
import Measure from "react-measure";
import { Dictionary } from "typescript-collections";
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
import { Utils as DashUtils } from '../../../Utils';
import { ColumnAttributeModel } from "../../northstar/core/attribute/AttributeModel";
import { AttributeTransformationModel } from "../../northstar/core/attribute/AttributeTransformationModel";
import { FilterModel } from '../../northstar/core/filter/FilterModel';
-import { DateTimeVisualBinRange } from "../../northstar/model/binRanges/DateTimeVisualBinRange";
import { NominalVisualBinRange } from "../../northstar/model/binRanges/NominalVisualBinRange";
-import { QuantitativeVisualBinRange } from "../../northstar/model/binRanges/QuantitativeVisualBinRange";
import { ChartType, VisualBinRange } from '../../northstar/model/binRanges/VisualBinRange';
import { VisualBinRangeHelper } from "../../northstar/model/binRanges/VisualBinRangeHelper";
import { AggregateBinRange, AggregateFunction, Bin, Brush, DoubleValueAggregateResult, HistogramResult, MarginAggregateParameters, MarginAggregateResult } from "../../northstar/model/idea/idea";
@@ -20,7 +19,6 @@ import { LABColor } from '../../northstar/utils/LABcolor';
import { PIXIRectangle } from "../../northstar/utils/MathUtil";
import { SizeConverter } from "../../northstar/utils/SizeConverter";
import { StyleConstants } from "../../northstar/utils/StyleContants";
-import { Main } from "../Main";
import { FieldView, FieldViewProps } from './FieldView';
import "./HistogramBox.scss";
@@ -51,7 +49,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
componentDidMount() {
reaction(() => [this.props.doc.Title],
() => {
- Main.Instance.GetAllNorthstarColumnAttributes().map(a => {
+ CurrentUserUtils.GetAllNorthstarColumnAttributes().map(a => {
if (a.displayName == this.props.doc.Title) {
var atmod = new ColumnAttributeModel(a);
this._histoOp = new HistogramOperation(new AttributeTransformationModel(atmod, AggregateFunction.None),