From 3655e529eef051e3d68f6e9c242d320be9b32906 Mon Sep 17 00:00:00 2001 From: madelinegr Date: Mon, 10 Jun 2019 15:45:12 -0400 Subject: end of day 6/10 eek --- src/client/views/collections/CollectionVideoView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/collections/CollectionVideoView.tsx') diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 7853544d5..c1a6ca44e 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -12,7 +12,7 @@ import { Id } from "../../../new_fields/FieldSymbols"; import { VideoBox } from "../nodes/VideoBox"; import { NumCast, Cast, StrCast } from "../../../new_fields/Types"; import { VideoField } from "../../../new_fields/URLField"; -import { SearchBox } from "../SearchBox"; +import { SearchBox } from "../search/SearchBox"; import { DocServer } from "../../DocServer"; import { Docs, DocUtils } from "../../documents/Documents"; -- cgit v1.2.3-70-g09d2 From d2c9550f23c4e5654822ac01b973bb965e3f6dec Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Fri, 14 Jun 2019 20:49:12 -0400 Subject: cleaned up Docs namespace and thoroughly documented DocServer.GetRefFields --- .vscode/settings.json | 3 +- src/client/DocServer.ts | 80 ++- src/client/documents/Documents.ts | 758 ++++++++++++--------- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/Main.tsx | 2 +- src/client/views/MainView.tsx | 24 +- src/client/views/SearchBox.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 386 ++++------- .../views/collections/CollectionSchemaView.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 16 +- .../views/collections/CollectionTreeView.tsx | 2 +- .../views/collections/CollectionVideoView.tsx | 2 +- .../views/collections/DockedFrameRenderer.tsx | 116 ++++ .../collections/collectionFreeForm/MarqueeView.tsx | 18 +- src/client/views/nodes/DocumentView.tsx | 4 +- src/mobile/ImageUpload.tsx | 4 +- src/new_fields/Doc.ts | 12 + src/new_fields/util.ts | 1 + .../authentication/models/current_user_utils.ts | 8 +- 19 files changed, 835 insertions(+), 607 deletions(-) create mode 100644 src/client/views/collections/DockedFrameRenderer.tsx (limited to 'src/client/views/collections/CollectionVideoView.tsx') diff --git a/.vscode/settings.json b/.vscode/settings.json index fc315ffaf..5df697fee 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,5 +9,6 @@ "editor.formatOnSave": true, "editor.detectIndentation": false, "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false, - "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true + "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true, + "search.usePCRE2": true } \ No newline at end of file diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index cbcf751ee..d759b4757 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -47,36 +47,81 @@ export namespace DocServer { } } + /** + * Given a list of Doc GUIDs, this utility function will asynchronously attempt to fetch each document + * associated with a given input id, first looking in the RefField cache and then communicating with + * the server if the document was not found there. + * + * @param ids the ids that map to the reqested documents + */ export async function GetRefFields(ids: string[]): Promise<{ [id: string]: Opt }> { const requestedIds: string[] = []; const waitingIds: string[] = []; const promises: Promise>[] = []; const map: { [id: string]: Opt } = {}; + + // 1) An initial pass through the cache to determine which documents need to be fetched, + // which are already in the process of being fetched and which already exist in the + // cache for (const id of ids) { const cached = _cache[id]; + if (cached === undefined) { + // NOT CACHED => we'll have to send a request to the server requestedIds.push(id); } else if (cached instanceof Promise) { + // BEING CACHED => someone else previously (likely recently) called GetRefFields, + // and requested one of the documents I'm looking for. Shouldn't fetch again, just + // wait until this promise is resolved (see the second to last line of the function) promises.push(cached); waitingIds.push(id); } else { + // CACHED => great, let's just add it to the field map map[id] = cached; } } - const prom = Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds).then(fields => { + + // 2) Synchronously, we emit a single callback to the server requesting the documents for the given ids. + // This returns a promise, which, when resolved, indicates that all the JSON serialized versions of + // the fields have been returned from the server + const fieldsReceived: Promise = Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds); + + // 3) When the serialized RefFields have been received, go head and begin deserializing them into objects. + // Here, once deserialized, we also invoke .proto to 'load' the documents' prototypes, which ensures that all + // future .proto calls won't have to go farther than the cache to get their actual value. + const fieldsDeserialized = fieldsReceived.then(async fields => { const fieldMap: { [id: string]: RefField } = {}; + const deserializedFields: any = []; for (const field of fields) { if (field !== undefined) { - fieldMap[field.id] = SerializationHelper.Deserialize(field); + // deserialize + let deserialized: any = SerializationHelper.Deserialize(field); + fieldMap[field.id] = deserialized; + deserializedFields.push(deserialized.proto); } } - + // this actually handles the loeading of prototypes + await Promise.all(deserializedFields); return fieldMap; }); - requestedIds.forEach(id => _cache[id] = prom.then(fields => fields[id])); - const fields = await prom; + + // 4) Here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache) + // we set the value at the field's id to a promise that will resolve to the field. + // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method). + requestedIds.forEach(id => _cache[id] = fieldsDeserialized.then(fields => fields[id])); + + // 5) At this point, all fields have a) been returned from the server and b) been deserialized into actual Field objects whose + // prototype documents, if any, have also been fetched and cached. + const fields = await fieldsDeserialized; + + // 6) With this confidence, we can now go through and update the cache at the ids of the fields that + // we explicitly had to fetch. To finish it off, we add whatever value we've come up with for a given + // id to the soon to be returned field mapping. requestedIds.forEach(id => { const field = fields[id]; + // either way, overwrite or delete any promises that we inserted as flags + // to indicate that the field was in the process of being fetched. Now everything + // should be an actual value within or entirely absent from the cache. if (field !== undefined) { _cache[id] = field; } else { @@ -84,14 +129,23 @@ export namespace DocServer { } map[id] = field; }); - await Promise.all(requestedIds.map(async id => { - const field = fields[id]; - if (field) { - await (field as any).proto; - } - })); - const otherFields = await Promise.all(promises); - waitingIds.forEach((id, index) => map[id] = otherFields[index]); + + // 7) Those promises we encountered in the else if of 1), which represent + // other callers having already submitted a request to the server for (a) document(s) + // in which we're interested, must still be awaited so that we can return the proper + // values for those as well. + // + // Fortunately, those other callers will also hit their own version of 6) and clean up + // the shared cache when these promises resolve, so all we have to do is... + const otherCallersFetching = await Promise.all(promises); + // ...extract the RefFields returned from the resolution of those promises and add them to our + // own map. + waitingIds.forEach((id, index) => map[id] = otherCallersFetching[index]); + + // Now, we return our completed mapping from all of the ids that were passed into the method + // to their actual RefField | undefined values. This return value either becomes the input + // argument to the caller's promise (i.e. GetRefFields.then(map => //do something with map...)) + // or it is the direct return result if the promise is awaited. return map; } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b346e1570..b10954636 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -18,7 +18,6 @@ import { action } from "mobx"; import { ColumnAttributeModel } from "../northstar/core/attribute/AttributeModel"; import { AttributeTransformationModel } from "../northstar/core/attribute/AttributeTransformationModel"; import { AggregateFunction } from "../northstar/model/idea/idea"; -import { Template } from "../views/Templates"; import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss"; import { IconBox } from "../views/nodes/IconBox"; import { Field, Doc, Opt } from "../../new_fields/Doc"; @@ -30,7 +29,7 @@ import { Cast, NumCast } from "../../new_fields/Types"; import { IconField } from "../../new_fields/IconField"; import { listSpec } from "../../new_fields/Schema"; import { DocServer } from "../DocServer"; -import { StrokeData, InkField } from "../../new_fields/InkField"; +import { InkField } from "../../new_fields/InkField"; import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; import { UndoManager } from "../util/UndoManager"; @@ -67,345 +66,486 @@ export interface DocumentOptions { dbDoc?: Doc; // [key: string]: Opt; } -const delegateKeys = ["x", "y", "width", "height", "panX", "panY"]; -export namespace DocUtils { - export function MakeLink(source: Doc, target: Doc) { - let protoSrc = source.proto ? source.proto : source; - let protoTarg = target.proto ? target.proto : target; - UndoManager.RunInBatch(() => { - let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); - //let linkDoc = new Doc; - linkDoc.proto!.title = "-link name-"; - linkDoc.proto!.linkDescription = ""; - linkDoc.proto!.linkTags = "Default"; +export namespace Docs { - linkDoc.proto!.linkedTo = target; - linkDoc.proto!.linkedToPage = target.curPage; - linkDoc.proto!.linkedFrom = source; - linkDoc.proto!.linkedFromPage = source.curPage; + export namespace Prototypes { - let linkedFrom = Cast(protoTarg.linkedFromDocs, listSpec(Doc)); - if (!linkedFrom) { - protoTarg.linkedFromDocs = linkedFrom = new List(); - } - linkedFrom.push(linkDoc); + // the complete list of document prototypes and their ids + export let textProto: Doc; const textProtoId = "textProto"; + export let histoProto: Doc; const histoProtoId = "histoProto"; + export let imageProto: Doc; const imageProtoId = "imageProto"; + export let webProto: Doc; const webProtoId = "webProto"; + export let collProto: Doc; const collProtoId = "collectionProto"; + export let kvpProto: Doc; const kvpProtoId = "kvpProto"; + export let videoProto: Doc; const videoProtoId = "videoProto"; + export let audioProto: Doc; const audioProtoId = "audioProto"; + export let pdfProto: Doc; const pdfProtoId = "pdfProto"; + export let iconProto: Doc; const iconProtoId = "iconProto"; - let linkedTo = Cast(protoSrc.linkedToDocs, listSpec(Doc)); - if (!linkedTo) { - protoSrc.linkedToDocs = linkedTo = new List(); + /** + * This function loads or initializes the prototype for each docment type. + * + * This is an asynchronous function because it has to attempt + * to fetch the prototype documents from the server. + * + * Once we have this object that maps the prototype ids to a potentially + * undefined document, we either initialize our private prototype + * variables with the document returned from the server or, if prototypes + * haven't been initialized, the newly initialized prototype document. + */ + export async function initialize(): Promise { + // non-guid string ids for each document prototype + let protoIds = [textProtoId, histoProtoId, collProtoId, imageProtoId, webProtoId, kvpProtoId, videoProtoId, audioProtoId, pdfProtoId, iconProtoId] + // fetch the actual prototype documents from the server + let actualProtos = await DocServer.GetRefFields(protoIds); + + // initialize prototype documents + textProto = actualProtos[textProtoId] as Doc || CreateTextProto(); + histoProto = actualProtos[histoProtoId] as Doc || CreateHistogramProto(); + collProto = actualProtos[collProtoId] as Doc || CreateCollectionProto(); + imageProto = actualProtos[imageProtoId] as Doc || CreateImageProto(); + webProto = actualProtos[webProtoId] as Doc || CreateWebProto(); + kvpProto = actualProtos[kvpProtoId] as Doc || CreateKVPProto(); + videoProto = actualProtos[videoProtoId] as Doc || CreateVideoProto(); + audioProto = actualProtos[audioProtoId] as Doc || CreateAudioProto(); + pdfProto = actualProtos[pdfProtoId] as Doc || CreatePdfProto(); + iconProto = actualProtos[iconProtoId] as Doc || CreateIconProto(); + } + + /** + * This is a convenience method that is used to initialize + * prototype documents for the first time. + * + * @param protoId the id of the prototype, indicating the specific prototype + * to initialize (see the *protoId list at the top of the namespace) + * @param title the prototype document's title, follows *-PROTO + * @param layout the layout key for this prototype and thus the + * layout key that all delegates will inherit + * @param options any value specified in the DocumentOptions object likewise + * becomes the default value for that key for all delegates + */ + function buildPrototype(protoId: string, title: string, layout: string, options: DocumentOptions): Doc { + return Doc.assign(new Doc(protoId, true), { ...options, title: title, layout: layout, baseLayout: layout }); + } + + // INDIVIDUAL INITIALIZERS + + function CreateImageProto(): Doc { + let defaultAttrs = { + x: 0, + y: 0, + nativeWidth: 600, + width: 300, + backgroundLayout: ImageBox.LayoutString(), + curPage: 0 + }; + return buildPrototype(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("annotations"), defaultAttrs); + } + + function CreateHistogramProto(): Doc { + let defaultAttrs = { + x: 0, + y: 0, + width: 300, + height: 300, + backgroundColor: "black", + backgroundLayout: + HistogramBox.LayoutString() + }; + return buildPrototype(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("annotations"), defaultAttrs); + } + + function CreateIconProto(): Doc { + let defaultAttrs = { + x: 0, + y: 0, + width: Number(MINIMIZED_ICON_SIZE), + height: Number(MINIMIZED_ICON_SIZE) + }; + return buildPrototype(iconProtoId, "ICON_PROTO", IconBox.LayoutString(), defaultAttrs); + } + + function CreateTextProto(): Doc { + let defaultAttrs = { + x: 0, + y: 0, + width: 300, + height: 150, + backgroundColor: "#f1efeb" + }; + return buildPrototype(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(), defaultAttrs); + } + + function CreatePdfProto(): Doc { + let defaultAttrs = { + x: 0, + y: 0, + nativeWidth: 1200, + width: 300, + backgroundLayout: PDFBox.LayoutString(), + curPage: 1 + }; + return buildPrototype(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"), defaultAttrs); + } + + function CreateWebProto(): Doc { + let defaultAttrs = { + x: 0, + y: 0, + width: 300, + height: 300 + }; + return buildPrototype(webProtoId, "WEB_PROTO", WebBox.LayoutString(), defaultAttrs); + } + + function CreateCollectionProto(): Doc { + let defaultAttrs = { + panX: 0, + panY: 0, + scale: 1, + width: 500, + height: 500 + }; + return buildPrototype(collProtoId, "COLLECTION_PROTO", CollectionView.LayoutString("data"), defaultAttrs); + } + + function CreateKVPProto(): Doc { + let defaultAttrs = { + x: 0, + y: 0, + width: 300, + height: 150 + }; + return buildPrototype(kvpProtoId, "KVP_PROTO", KeyValueBox.LayoutString(), defaultAttrs); + } + + function CreateVideoProto(): Doc { + let defaultAttrs = { + x: 0, + y: 0, + nativeWidth: 600, + width: 300, + backgroundLayout: VideoBox.LayoutString(), + curPage: 0 + }; + return buildPrototype(videoProtoId, "VIDEO_PROTO", CollectionVideoView.LayoutString("annotations"), defaultAttrs); + } + + function CreateAudioProto(): Doc { + let defaultAttrs = { + x: 0, + y: 0, + width: 300, + height: 150 } - linkedTo.push(linkDoc); - return linkDoc; - }, "make link"); + return buildPrototype(audioProtoId, "AUDIO_PROTO", AudioBox.LayoutString(), defaultAttrs); + } } + /** + * Encapsulates the factory used to create new document instances + * delegated from top-level prototypes + */ + export namespace Create { -} + const delegateKeys = ["x", "y", "width", "height", "panX", "panY"]; -export namespace Docs { - let textProto: Doc; - let histoProto: Doc; - let imageProto: Doc; - let webProto: Doc; - let collProto: Doc; - let kvpProto: Doc; - let videoProto: Doc; - let audioProto: Doc; - let pdfProto: Doc; - let iconProto: Doc; - const textProtoId = "textProto"; - const histoProtoId = "histoProto"; - const pdfProtoId = "pdfProto"; - const imageProtoId = "imageProto"; - const webProtoId = "webProto"; - const collProtoId = "collectionProto"; - const kvpProtoId = "kvpProto"; - const videoProtoId = "videoProto"; - const audioProtoId = "audioProto"; - const iconProtoId = "iconProto"; - - export function initProtos(): Promise { - return DocServer.GetRefFields([textProtoId, histoProtoId, collProtoId, imageProtoId, webProtoId, kvpProtoId, videoProtoId, audioProtoId, pdfProtoId, iconProtoId]).then(fields => { - textProto = fields[textProtoId] as Doc || CreateTextPrototype(); - histoProto = fields[histoProtoId] as Doc || CreateHistogramPrototype(); - collProto = fields[collProtoId] as Doc || CreateCollectionPrototype(); - imageProto = fields[imageProtoId] as Doc || CreateImagePrototype(); - webProto = fields[webProtoId] as Doc || CreateWebPrototype(); - kvpProto = fields[kvpProtoId] as Doc || CreateKVPPrototype(); - videoProto = fields[videoProtoId] as Doc || CreateVideoPrototype(); - audioProto = fields[audioProtoId] as Doc || CreateAudioPrototype(); - pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype(); - iconProto = fields[iconProtoId] as Doc || CreateIconPrototype(); - }); - } + /** + * This function receives the relevant document prototype and uses + * it to create a new of that base-level prototype, or the + * underlying data document, which it then delegates again + * to create the view document. + * + * It also takes the opportunity to register the user + * that created the document and the time of creation. + * + * @param proto the specific document prototype off of which to model + * this new instance (textProto, imageProto, etc.) + * @param data the Field to store at this new instance's data key + * @param options any initial values to provide for this new instance + * @param delegId if applicable, an existing document id. If undefined, Doc's + * constructor just generates a new GUID. This is currently used + * only when creating a DockDocument from the current user's already existing + * main document. + */ + function CreateInstanceFromProto(proto: Doc, data: Field, options: DocumentOptions, delegId?: string) { + const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys); - function setupPrototypeOptions(protoId: string, title: string, layout: string, options: DocumentOptions): Doc { - return Doc.assign(new Doc(protoId, true), { ...options, title: title, layout: layout, baseLayout: layout }); - } - function SetInstanceOptions(doc: Doc, options: DocumentOptions, value: U) { - const deleg = Doc.MakeDelegate(doc); - deleg.data = value; - return Doc.assign(deleg, options); - } - function SetDelegateOptions(doc: Doc, options: DocumentOptions, id?: string) { - const deleg = Doc.MakeDelegate(doc, id); - return Doc.assign(deleg, options); - } + if (!("author" in protoProps)) { + protoProps.author = CurrentUserUtils.email; + } - function CreateImagePrototype(): Doc { - let imageProto = setupPrototypeOptions(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("annotations"), - { x: 0, y: 0, nativeWidth: 600, width: 300, backgroundLayout: ImageBox.LayoutString(), curPage: 0 }); - return imageProto; - } + if (!("creationDate" in protoProps)) { + protoProps.creationDate = new DateField; + } - function CreateHistogramPrototype(): Doc { - let histoProto = setupPrototypeOptions(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("annotations"), - { x: 0, y: 0, width: 300, height: 300, backgroundColor: "black", backgroundLayout: HistogramBox.LayoutString() }); - return histoProto; - } - function CreateIconPrototype(): Doc { - let iconProto = setupPrototypeOptions(iconProtoId, "ICON_PROTO", IconBox.LayoutString(), - { x: 0, y: 0, width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE) }); - return iconProto; - } - function CreateTextPrototype(): Doc { - let textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(), - { x: 0, y: 0, width: 300, height: 150, backgroundColor: "#f1efeb" }); - return textProto; - } - function CreatePdfPrototype(): Doc { - let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"), - { x: 0, y: 0, nativeWidth: 1200, width: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 }); - return pdfProto; - } - function CreateWebPrototype(): Doc { - let webProto = setupPrototypeOptions(webProtoId, "WEB_PROTO", WebBox.LayoutString(), - { x: 0, y: 0, width: 300, height: 300 }); - return webProto; - } - function CreateCollectionPrototype(): Doc { - let collProto = setupPrototypeOptions(collProtoId, "COLLECTION_PROTO", CollectionView.LayoutString("data"), - { panX: 0, panY: 0, scale: 1, width: 500, height: 500 }); - return collProto; - } + protoProps.isPrototype = true; - function CreateKVPPrototype(): Doc { - let kvpProto = setupPrototypeOptions(kvpProtoId, "KVP_PROTO", KeyValueBox.LayoutString(), - { x: 0, y: 0, width: 300, height: 150 }); - return kvpProto; - } - function CreateVideoPrototype(): Doc { - let videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionVideoView.LayoutString("annotations"), - { x: 0, y: 0, nativeWidth: 600, width: 300, backgroundLayout: VideoBox.LayoutString(), curPage: 0 }); - return videoProto; - } - function CreateAudioPrototype(): Doc { - let audioProto = setupPrototypeOptions(audioProtoId, "AUDIO_PROTO", AudioBox.LayoutString(), - { x: 0, y: 0, width: 300, height: 150 }); - return audioProto; - } + let dataDoc = MakeDataDelegate(proto, protoProps, data); + let viewDoc = Doc.MakeDelegate(dataDoc, delegId); - function CreateInstance(proto: Doc, data: Field, options: DocumentOptions, delegId?: string) { - const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys); - if (!("author" in protoProps)) { - protoProps.author = CurrentUserUtils.email; + return Doc.assign(viewDoc, delegateProps); } - if (!("creationDate" in protoProps)) { - protoProps.creationDate = new DateField; + + /** + * This function receives the relevant top level document prototype + * and models a new instance by delegating from it. + * + * Note that it stores the data it recieves at the delegate's data key, + * and applies any document options to this new delegate / instance. + * @param proto the prototype from which to model this new delegate + * @param options initial values to apply to this new delegate + * @param value the data to store in this new delegate + */ + function MakeDataDelegate(proto: Doc, options: DocumentOptions, value: D) { + const deleg = Doc.MakeDelegate(proto); + deleg.data = value; + return Doc.assign(deleg, options); } - protoProps.isPrototype = true; - return SetDelegateOptions(SetInstanceOptions(proto, protoProps, data), delegateProps, delegId); - } + export function ImageDocument(url: string, options: DocumentOptions = {}) { + let inst = CreateInstanceFromProto(Prototypes.imageProto, new ImageField(new URL(url)), { title: path.basename(url), ...options }); + requestImageSize(window.origin + RouteStore.corsProxy + "/" + url) + .then((size: any) => { + let aspect = size.height / size.width; + if (!inst.proto!.nativeWidth) { + inst.proto!.nativeWidth = size.width; + } + inst.proto!.nativeHeight = Number(inst.proto!.nativeWidth!) * aspect; + inst.proto!.height = NumCast(inst.proto!.width) * aspect; + }) + .catch((err: any) => console.log(err)); + return inst; - export function ImageDocument(url: string, options: DocumentOptions = {}) { - let inst = CreateInstance(imageProto, new ImageField(new URL(url)), { title: path.basename(url), ...options }); - requestImageSize(window.origin + RouteStore.corsProxy + "/" + url) - .then((size: any) => { - let aspect = size.height / size.width; - if (!inst.proto!.nativeWidth) { - inst.proto!.nativeWidth = size.width; + // let doc = SetInstanceOptions(GetImagePrototype(), { ...options, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] }, + // [new URL(url), ImageField]); + // doc.SetText(KeyStore.Caption, "my caption..."); + // doc.SetText(KeyStore.BackgroundLayout, EmbeddedCaption()); + // doc.SetText(KeyStore.OverlayLayout, FixedCaption()); + // return doc; + } + + export function VideoDocument(url: string, options: DocumentOptions = {}) { + return CreateInstanceFromProto(Prototypes.videoProto, new VideoField(new URL(url)), options); + } + + export function AudioDocument(url: string, options: DocumentOptions = {}) { + return CreateInstanceFromProto(Prototypes.audioProto, new AudioField(new URL(url)), options); + } + + export function HistogramDocument(histoOp: HistogramOperation, options: DocumentOptions = {}) { + return CreateInstanceFromProto(Prototypes.histoProto, new HistogramField(histoOp), options); + } + + export function TextDocument(options: DocumentOptions = {}) { + return CreateInstanceFromProto(Prototypes.textProto, "", options); + } + + export function IconDocument(icon: string, options: DocumentOptions = {}) { + return CreateInstanceFromProto(Prototypes.iconProto, new IconField(icon), options); + } + + export function PdfDocument(url: string, options: DocumentOptions = {}) { + return CreateInstanceFromProto(Prototypes.pdfProto, new PdfField(new URL(url)), options); + } + + export async function DBDocument(url: string, options: DocumentOptions = {}, columnOptions: 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 = Docs.Create.TreeDocument([], { ...options, nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: schema.displayName! }); + let schemaDocuments = Cast(schemaDoc.data, listSpec(Doc), []); + if (!schemaDocuments) { + return; } - inst.proto!.nativeHeight = Number(inst.proto!.nativeWidth!) * aspect; - inst.proto!.height = NumCast(inst.proto!.width) * aspect; - }) - .catch((err: any) => console.log(err)); - return inst; - // let doc = SetInstanceOptions(GetImagePrototype(), { ...options, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] }, - // [new URL(url), ImageField]); - // doc.SetText(KeyStore.Caption, "my caption..."); - // doc.SetText(KeyStore.BackgroundLayout, EmbeddedCaption()); - // doc.SetText(KeyStore.OverlayLayout, FixedCaption()); - // return doc; - } - export function VideoDocument(url: string, options: DocumentOptions = {}) { - return CreateInstance(videoProto, new VideoField(new URL(url)), options); - } - export function AudioDocument(url: string, options: DocumentOptions = {}) { - return CreateInstance(audioProto, new AudioField(new URL(url)), options); - } + CurrentUserUtils.AddNorthstarSchema(schema, schemaDoc); + const docs = schemaDocuments; + CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => { + DocServer.GetRefField(attr.displayName! + ".alias").then(action((field: Opt) => { + if (field instanceof Doc) { + docs.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)); + docs.push(Docs.Create.HistogramDocument(histoOp, { ...columnOptions, width: 200, height: 200, title: attr.displayName! })); + } + })); + }); + return schemaDoc; + } + return Docs.Create.TreeDocument([], { width: 50, height: 100, title: schemaName }); + } - export function HistogramDocument(histoOp: HistogramOperation, options: DocumentOptions = {}) { - return CreateInstance(histoProto, new HistogramField(histoOp), options); - } - export function TextDocument(options: DocumentOptions = {}) { - return CreateInstance(textProto, "", options); - } - export function IconDocument(icon: string, options: DocumentOptions = {}) { - return CreateInstance(iconProto, new IconField(icon), options); - } - export function PdfDocument(url: string, options: DocumentOptions = {}) { - return CreateInstance(pdfProto, new PdfField(new URL(url)), options); - } + export function WebDocument(url: string, options: DocumentOptions = {}) { + return CreateInstanceFromProto(Prototypes.webProto, new WebField(new URL(url)), options); + } + + export function HtmlDocument(html: string, options: DocumentOptions = {}) { + return CreateInstanceFromProto(Prototypes.webProto, new HtmlField(html), options); + } - export async function DBDocument(url: string, options: DocumentOptions = {}, columnOptions: 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 = Docs.TreeDocument([], { ...options, nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: schema.displayName! }); - let schemaDocuments = Cast(schemaDoc.data, listSpec(Doc), []); - if (!schemaDocuments) { - return; + export function KVPDocument(document: Doc, options: DocumentOptions = {}) { + return CreateInstanceFromProto(Prototypes.kvpProto, document, { title: document.title + ".kvp", ...options }); + } + + export function FreeformDocument(documents: Array, options: DocumentOptions, makePrototype: boolean = true) { + if (!makePrototype) { + return MakeDataDelegate(Prototypes.collProto, { ...options, viewType: CollectionViewType.Freeform }, new List(documents)); } - CurrentUserUtils.AddNorthstarSchema(schema, schemaDoc); - const docs = schemaDocuments; - CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => { - DocServer.GetRefField(attr.displayName! + ".alias").then(action((field: Opt) => { - if (field instanceof Doc) { - docs.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)); - docs.push(Docs.HistogramDocument(histoOp, { ...columnOptions, width: 200, height: 200, title: attr.displayName! })); + return CreateInstanceFromProto(Prototypes.collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Freeform }); + } + + export function SchemaDocument(schemaColumns: string[], documents: Array, options: DocumentOptions) { + return CreateInstanceFromProto(Prototypes.collProto, new List(documents), { schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema }); + } + + export function TreeDocument(documents: Array, options: DocumentOptions) { + return CreateInstanceFromProto(Prototypes.collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Tree }); + } + + export function StackingDocument(documents: Array, options: DocumentOptions) { + return CreateInstanceFromProto(Prototypes.collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Stacking }); + } + + export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { + return CreateInstanceFromProto(Prototypes.collProto, new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); + } + + export type DocConfig = { + doc: Doc, + initialWidth?: number + } + + export function StandardCollectionDockingDocument(configs: Array, options: DocumentOptions, id?: string, type: string = "row") { + let layoutConfig = { + content: [ + { + type: type, + content: [ + ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, config.initialWidth)) + ] } - })); - }); - return schemaDoc; + ] + }; + return DockDocument(configs.map(c => c.doc), JSON.stringify(layoutConfig), options, id); } - return Docs.TreeDocument([], { width: 50, height: 100, title: schemaName }); - } - export function WebDocument(url: string, options: DocumentOptions = {}) { - return CreateInstance(webProto, new WebField(new URL(url)), options); - } - export function HtmlDocument(html: string, options: DocumentOptions = {}) { - return CreateInstance(webProto, new HtmlField(html), options); - } - export function KVPDocument(document: Doc, options: DocumentOptions = {}) { - return CreateInstance(kvpProto, document, { title: document.title + ".kvp", ...options }); - } - export function FreeformDocument(documents: Array, options: DocumentOptions, makePrototype: boolean = true) { - if (!makePrototype) { - return SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Freeform }, new List(documents)); + + export function CaptionDocument(doc: Doc) { + const captionDoc = Doc.MakeAlias(doc); + captionDoc.overlayLayout = Templating.FixedCaption(); + captionDoc.width = Cast(doc.width, "number", 0); + captionDoc.height = Cast(doc.height, "number", 0); + return captionDoc; } - return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Freeform }); - } - export function SchemaDocument(schemaColumns: string[], documents: Array, options: DocumentOptions) { - return CreateInstance(collProto, new List(documents), { schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema }); - } - export function TreeDocument(documents: Array, options: DocumentOptions) { - return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Tree }); - } - export function StackingDocument(documents: Array, options: DocumentOptions) { - return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Stacking }); - } - export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { - return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); - } - export type DocConfig = { - doc: Doc, - initialWidth?: number - } - export function StandardCollectionDockingDocument(configs: Array, options: DocumentOptions, id?: string, type: string = "row") { - let layoutConfig = { - content: [ - { - type: type, - content: [ - ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, config.initialWidth)) - ] - } - ] - }; - return DockDocument(configs.map(c => c.doc), JSON.stringify(layoutConfig), options, id); } - export function CaptionDocument(doc: Doc) { - const captionDoc = Doc.MakeAlias(doc); - captionDoc.overlayLayout = FixedCaption(); - captionDoc.width = Cast(doc.width, "number", 0); - captionDoc.height = Cast(doc.height, "number", 0); - return captionDoc; - } + export namespace Templating { - // example of custom display string for an image that shows a caption. - function EmbeddedCaption() { - return `
-
` - + ImageBox.LayoutString() + - `
-
` - + FormattedTextBox.LayoutString("caption") + - `
-
`; - } - export function FixedCaption(fieldName: string = "caption") { - return `
-
` - + FormattedTextBox.LayoutString(fieldName) + - `
-
`; - } + // example of custom display string for an image that shows a caption. + export function EmbeddedCaption() { + return `
+
` + + ImageBox.LayoutString() + + `
+
` + + FormattedTextBox.LayoutString("caption") + + `
+
`; + } - function OuterCaption() { - return (` -
-
- {layout} -
-
- -
-
- `); - } - function InnerCaption() { - return (` -
-
- {layout} -
-
- -
-
+ export function FixedCaption(fieldName: string = "caption") { + return `
+
` + + FormattedTextBox.LayoutString(fieldName) + + `
+
`; + } + + export function OuterCaption() { + return (` +
+
+ {layout} +
+
+ +
+
`); - } + } - /* + export function InnerCaption() { + return (` +
+
+ {layout} +
+
+ +
+
+ `); + } - this template requires an additional style setting on the collectionView-cont to make the layout relative - -.collectionView-cont { - position: relative; - width: 100%; - height: 100%; -} - */ - function Percentaption() { - return (` -
-
- {layout} -
-
- -
-
+ /* + this template requires an additional style setting on the collectionView-cont to make the layout relative + .collectionView-cont { + position: relative; + width: 100%; + height: 100%; + } + */ + export function PercentCaption() { + return (` +
+
+ {layout} +
+
+ +
+
`); + } + + } +} + +export namespace DocUtils { + + export function MakeLink(source: Doc, target: Doc) { + let protoSrc = source.proto ? source.proto : source; + let protoTarg = target.proto ? target.proto : target; + UndoManager.RunInBatch(() => { + let linkDoc = Docs.Create.TextDocument({ width: 100, height: 30, borderRounding: -1 }); + //let linkDoc = new Doc; + linkDoc.proto!.title = "-link name-"; + linkDoc.proto!.linkDescription = ""; + linkDoc.proto!.linkTags = "Default"; + + linkDoc.proto!.linkedTo = target; + linkDoc.proto!.linkedToPage = target.curPage; + linkDoc.proto!.linkedFrom = source; + linkDoc.proto!.linkedFromPage = source.curPage; + + let linkedFrom = Cast(protoTarg.linkedFromDocs, listSpec(Doc)); + if (!linkedFrom) { + protoTarg.linkedFromDocs = linkedFrom = new List(); + } + linkedFrom.push(linkDoc); + + let linkedTo = Cast(protoSrc.linkedToDocs, listSpec(Doc)); + if (!linkedTo) { + protoSrc.linkedToDocs = linkedTo = new List(); + } + linkedTo.push(linkDoc); + return linkDoc; + }, "make link"); } + } \ No newline at end of file diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index da9b1253e..787033455 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -274,7 +274,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> @undoBatch @action createIcon = (selected: DocumentView[], layoutString: string): Doc => { let doc = selected[0].props.Document; - let iconDoc = Docs.IconDocument(layoutString); + let iconDoc = Docs.Create.IconDocument(layoutString); iconDoc.isButton = true; iconDoc.proto!.title = selected.length > 1 ? "-multiple-.icon" : StrCast(doc.title) + ".icon"; iconDoc.labelField = selected.length > 1 ? undefined : this._fieldKey; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 3d9750a85..98b14f9c8 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -5,7 +5,7 @@ import * as ReactDOM from 'react-dom'; import * as React from 'react'; (async () => { - await Docs.initProtos(); + await Docs.Prototypes.initialize(); await CurrentUserUtils.loadCurrentUser(); ReactDOM.render(, document.getElementById('root')); })(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 426e2440a..984db0426 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -55,7 +55,7 @@ export class MainView extends React.Component { private set mainContainer(doc: Opt) { if (doc) { if (!("presentationView" in doc)) { - doc.presentationView = Docs.TreeDocument([], { title: "Presentation" }); + doc.presentationView = Docs.Create.TreeDocument([], { title: "Presentation" }); } CurrentUserUtils.UserDocument.activeWorkspace = doc; } @@ -151,12 +151,12 @@ export class MainView extends React.Component { createNewWorkspace = async (id?: string) => { const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc)); if (list) { - let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, width: this.pwidth * .7, height: this.pheight, title: `WS collection ${list.length + 1}` }); + let freeformDoc = Docs.Create.FreeformDocument([], { x: 0, y: 400, width: this.pwidth * .7, height: this.pheight, title: `WS collection ${list.length + 1}` }); let configs = [ { doc: CurrentUserUtils.UserDocument, initialWidth: 150 }, { doc: freeformDoc, initialWidth: 600 } ] - let mainDoc = Docs.StandardCollectionDockingDocument(configs, { title: `Workspace ${list.length + 1}` }, id); + let mainDoc = Docs.Create.StandardCollectionDockingDocument(configs, { title: `Workspace ${list.length + 1}` }, id); list.push(mainDoc); // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) setTimeout(() => { @@ -242,18 +242,18 @@ export class MainView extends React.Component { let audiourl = "http://techslides.com/demos/samples/sample.mp3"; let videourl = "http://techslides.com/demos/sample-videos/small.mp4"; - let addTextNode = action(() => Docs.TextDocument({ borderRounding: -1, width: 200, height: 200, title: "a text note" })); - let addColNode = action(() => Docs.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" })); - let addDockingNode = action(() => Docs.StandardCollectionDockingDocument([{ doc: addColNode(), initialWidth: 200 }], { width: 200, height: 200, title: "a nested docking freeform collection" })); - let addSchemaNode = action(() => Docs.SchemaDocument(["title"], [], { width: 200, height: 200, title: "a schema collection" })); + let addTextNode = action(() => Docs.Create.TextDocument({ borderRounding: -1, width: 200, height: 200, title: "a text note" })); + let addColNode = action(() => Docs.Create.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" })); + let addDockingNode = action(() => Docs.Create.StandardCollectionDockingDocument([{ doc: addColNode(), initialWidth: 200 }], { width: 200, height: 200, title: "a nested docking freeform collection" })); + let addSchemaNode = action(() => Docs.Create.SchemaDocument(["title"], [], { width: 200, height: 200, title: "a schema collection" })); let addTreeNode = action(() => CurrentUserUtils.UserDocument); //let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" })); // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", dropAction: "copy" })); - let addVideoNode = action(() => Docs.VideoDocument(videourl, { width: 200, title: "video node" })); - let addPDFNode = action(() => Docs.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" })); - let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })); - let addWebNode = action(() => Docs.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" })); - let addAudioNode = action(() => Docs.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" })); + let addVideoNode = action(() => Docs.Create.VideoDocument(videourl, { width: 200, title: "video node" })); + let addPDFNode = action(() => Docs.Create.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" })); + let addImageNode = action(() => Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })); + let addWebNode = action(() => Docs.Create.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" })); + let addAudioNode = action(() => Docs.Create.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" })); let btns: [React.RefObject, IconName, string, () => Doc][] = [ [React.createRef(), "font", "Add Textbox", addTextNode], diff --git a/src/client/views/SearchBox.tsx b/src/client/views/SearchBox.tsx index 63d2065e2..7164d98a4 100644 --- a/src/client/views/SearchBox.tsx +++ b/src/client/views/SearchBox.tsx @@ -166,7 +166,7 @@ export class SearchBox extends React.Component { y += 300; } } - return Docs.FreeformDocument(docs, { width: 400, height: 400, panX: 175, panY: 175, backgroundColor: "grey", title: `Search Docs: "${this.searchString}"` }); + return Docs.Create.FreeformDocument(docs, { width: 400, height: 400, panX: 175, panY: 175, backgroundColor: "grey", title: `Search Docs: "${this.searchString}"` }); } // Useful queries: diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index dfb8fac35..e2bcb10ec 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -6,50 +6,32 @@ import * as ReactDOM from 'react-dom'; import Measure, { ContentRect } from "react-measure"; import * as GoldenLayout from "../../../client/goldenLayout"; import { Doc, Field, Opt, DocListCast } from "../../../new_fields/Doc"; -import { FieldId } from "../../../new_fields/RefField"; import { listSpec } from "../../../new_fields/Schema"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { emptyFunction, returnTrue, Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { DragLinksAsDocuments, DragManager } from "../../util/DragManager"; -import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from "../../util/UndoManager"; -import { DocumentView } from "../nodes/DocumentView"; import "./CollectionDockingView.scss"; import { SubCollectionViewProps } from "./CollectionSubView"; import React = require("react"); import { ParentDocSelector } from './ParentDocumentSelector'; import { DocumentManager } from '../../util/DocumentManager'; -import { CollectionViewType } from './CollectionBaseView'; import { Id } from '../../../new_fields/FieldSymbols'; -import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; +import { DockedFrameRenderer } from './DockedFrameRenderer'; @observer export class CollectionDockingView extends React.Component { public static TopLevel: CollectionDockingView; - public static makeDocumentConfig(document: Doc, width?: number) { - return { - type: 'react-component', - component: 'DocumentFrameRenderer', - title: document.title, - width: width, - props: { - documentId: document[Id], - } - }; - } - - private makeDocConfig = (document: Doc, width?: number) => { - const config = CollectionDockingView.makeDocumentConfig(document, width); - (config.props as any).parent = this; - return config; - } - private _goldenLayout: any = null; private _containerRef = React.createRef(); + reactionDisposer?: IReactionDisposer; + _removedDocs: Doc[] = []; private _flush: boolean = false; private _ignoreStateChange = ""; private _isPointerDown = false; + hack: boolean = false; + undohack: any = null; constructor(props: SubCollectionViewProps) { super(props); @@ -57,32 +39,93 @@ export class CollectionDockingView extends React.Component - CollectionDockingView.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener. - onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 })); + + componentDidMount: () => void = () => { + if (this._containerRef.current) { + this.reactionDisposer = reaction( + () => StrCast(this.props.Document.dockingConfig), + () => { + if (!this._goldenLayout || this._ignoreStateChange !== this.retrieveConfiguration()) { + // Because this is in a set timeout, if this component unmounts right after mounting, + // we will leak a GoldenLayout, because we try to destroy it before we ever create it + setTimeout(() => this.setupGoldenLayout(), 1); + } + this._ignoreStateChange = ""; + }, { fireImmediately: true }); + + // window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window + } } - private openFullScreen = (document: Doc) => { - let newItemStackConfig = { - type: 'stack', - content: [this.makeDocConfig(document)] - }; - var docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout); - this._goldenLayout.root.contentItems[0].addChild(docconfig); - docconfig.callDownwards('_$init'); - this._goldenLayout._$maximiseItem(docconfig); - this._ignoreStateChange = this.retrieveConfiguration(); - this.stateChanged(); + componentWillUnmount: () => void = () => { + try { + this._goldenLayout.unbind('itemDropped', this.itemDropped); + this._goldenLayout.unbind('tabCreated', this.tabCreated); + this._goldenLayout.unbind('stackCreated', this.stackCreated); + this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); + } catch (e) { + console.log("Unable to unbind Golden Layout event listener...", e); + } + if (this._goldenLayout) this._goldenLayout.destroy(); + this._goldenLayout = null; + + if (this.reactionDisposer) { + this.reactionDisposer(); + } } - @action - public static OpenFullScreen(document: Doc, dockingView: CollectionDockingView = CollectionDockingView.TopLevel) { - dockingView.openFullScreen(document); + setupGoldenLayout() { + var config = StrCast(this.props.Document.dockingConfig); + if (config) { + if (!this._goldenLayout) { + this.initializeConfiguration(config); + } + else { + if (config === this.retrieveConfiguration()) { + return; + } + try { + this._goldenLayout.unbind('itemDropped', this.itemDropped); + this._goldenLayout.unbind('tabCreated', this.tabCreated); + this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); + this._goldenLayout.unbind('stackCreated', this.stackCreated); + } catch (e) { } + this._goldenLayout.destroy(); + this.initializeConfiguration(config); + } + this._goldenLayout.on('itemDropped', this.itemDropped); + this._goldenLayout.on('tabCreated', this.tabCreated); + this._goldenLayout.on('tabDestroyed', this.tabDestroyed); + this._goldenLayout.on('stackCreated', this.stackCreated); + this._goldenLayout.registerComponent('DocumentFrameRenderer', DockedFrameRenderer); + this._goldenLayout.container = this._containerRef.current; + if (this._goldenLayout.config.maximisedItemId === '__glMaximised') { + try { + this._goldenLayout.config.root.getItemsById(this._goldenLayout.config.maximisedItemId)[0].toggleMaximise(); + } catch (e) { + this._goldenLayout.config.maximisedItemId = null; + } + } + this._goldenLayout.init(); + } + } + + private makeDocConfig = (document: Doc, width?: number) => { + const config = CollectionDockingView.makeDocumentConfig(document, width); + (config.props as any).parent = this; + return config; + } + + public static makeDocumentConfig(document: Doc, width?: number) { + return { + type: 'react-component', + component: 'DocumentFrameRenderer', + title: document.title, + width: width, + props: { + documentId: document[Id], + } + }; } initializeConfiguration = (configText: string) => { @@ -109,44 +152,29 @@ export class CollectionDockingView extends React.Component { - let retVal = false; - if (dockingView._goldenLayout.root.contentItems[0].isRow) { - retVal = Array.from(dockingView._goldenLayout.root.contentItems[0].contentItems).some((child: any) => { - if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" && - Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)!.Document, document)) { - child.contentItems[0].remove(); - dockingView.layoutChanged(document); - return true; - } else { - Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => { - if (Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)!.Document, document)) { - child.contentItems[j].remove(); - child.config.activeItemIndex = Math.max(child.contentItems.length - 1, 0); - let docs = Cast(dockingView.props.Document.data, listSpec(Doc)); - docs && docs.indexOf(document) !== -1 && docs.splice(docs.indexOf(document), 1); - return true; - } - return false; - }); - } - return false; - }); - } - if (retVal) { - dockingView.stateChanged(); - } - return retVal; + public StartOtherDrag(dragDocs: Doc[], e: any) { + this.hack = true; + this.undohack = UndoManager.StartBatch("goldenDrag"); + dragDocs.map(dragDoc => + CollectionDockingView.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener. + onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 })); } @action - layoutChanged(removed?: Doc) { - this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]); - this._goldenLayout.emit('stateChanged'); + public static OpenFullScreen(document: Doc, dockingView: CollectionDockingView = CollectionDockingView.TopLevel) { + dockingView.openFullScreen(document); + } + + private openFullScreen = (document: Doc) => { + let newItemStackConfig = { + type: 'stack', + content: [this.makeDocConfig(document)] + }; + var docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout); + this._goldenLayout.root.contentItems[0].addChild(docconfig); + docconfig.callDownwards('_$init'); + this._goldenLayout._$maximiseItem(docconfig); this._ignoreStateChange = this.retrieveConfiguration(); - if (removed) CollectionDockingView.TopLevel._removedDocs.push(removed); this.stateChanged(); } @@ -209,75 +237,47 @@ export class CollectionDockingView extends React.Component { + let retVal = false; + if (dockingView._goldenLayout.root.contentItems[0].isRow) { + retVal = Array.from(dockingView._goldenLayout.root.contentItems[0].contentItems).some((child: any) => { + if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" && + Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)!.Document, document)) { + child.contentItems[0].remove(); + dockingView.layoutChanged(document); + return true; + } else { + Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => { + if (Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)!.Document, document)) { + child.contentItems[j].remove(); + child.config.activeItemIndex = Math.max(child.contentItems.length - 1, 0); + let docs = Cast(dockingView.props.Document.data, listSpec(Doc)); + docs && docs.indexOf(document) !== -1 && docs.splice(docs.indexOf(document), 1); + return true; + } + return false; + }); } - } - this._goldenLayout.init(); + return false; + }); } - } - reactionDisposer?: IReactionDisposer; - componentDidMount: () => void = () => { - if (this._containerRef.current) { - this.reactionDisposer = reaction( - () => StrCast(this.props.Document.dockingConfig), - () => { - if (!this._goldenLayout || this._ignoreStateChange !== this.retrieveConfiguration()) { - // Because this is in a set timeout, if this component unmounts right after mounting, - // we will leak a GoldenLayout, because we try to destroy it before we ever create it - setTimeout(() => this.setupGoldenLayout(), 1); - } - this._ignoreStateChange = ""; - }, { fireImmediately: true }); - - // window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window + if (retVal) { + dockingView.stateChanged(); } + return retVal; } - componentWillUnmount: () => void = () => { - try { - this._goldenLayout.unbind('itemDropped', this.itemDropped); - this._goldenLayout.unbind('tabCreated', this.tabCreated); - this._goldenLayout.unbind('stackCreated', this.stackCreated); - this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); - } catch (e) { - } - if (this._goldenLayout) this._goldenLayout.destroy(); - this._goldenLayout = null; - // window.removeEventListener('resize', this.onResize); - - if (this.reactionDisposer) { - this.reactionDisposer(); - } + @action + layoutChanged(removed?: Doc) { + this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]); + this._goldenLayout.emit('stateChanged'); + this._ignoreStateChange = this.retrieveConfiguration(); + if (removed) CollectionDockingView.TopLevel._removedDocs.push(removed); + this.stateChanged(); } + @action onResize = (size: ContentRect) => { // bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed @@ -387,6 +387,9 @@ export class CollectionDockingView extends React.Component { + console.log("DROPPPP THE BASS!", e); + } ReactDOM.render( CollectionDockingView.AddTab(stack, doc)} />, upDiv); tab.reactComponents = [upDiv]; tab.element.append(upDiv); @@ -422,7 +425,6 @@ export class CollectionDockingView extends React.Component { //stack.header.controlsContainer.find('.lm_popout').hide(); @@ -462,102 +464,4 @@ export class CollectionDockingView extends React.Component { - _mainCont = React.createRef(); - @observable private _panelWidth = 0; - @observable private _panelHeight = 0; - @observable private _document: Opt; - private get parentProps(): SubCollectionViewProps { - return this.props.parent.props; - } - - get _stack(): any { - let parent = this.props.glContainer.parent.parent; - if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) - return parent.parent.contentItems[1]; - return parent; - } - constructor(props: any) { - super(props); - DocServer.GetRefField(this.props.documentId).then(action((f: Opt) => this._document = f as Doc)); - } - - nativeWidth = () => NumCast(this._document!.nativeWidth, this._panelWidth); - nativeHeight = () => NumCast(this._document!.nativeHeight, this._panelHeight); - contentScaling = () => { - const nativeH = this.nativeHeight(); - const nativeW = this.nativeWidth(); - let wscale = this._panelWidth / nativeW; - return wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale; - } - - ScreenToLocalTransform = () => { - if (this._mainCont.current && this._mainCont.current.children) { - let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current.children[0].firstChild as HTMLElement); - scale = Utils.GetScreenTransform(this._mainCont.current).scale; - return this.parentProps.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale); - } - return Transform.Identity(); - } - get scaleToFitMultiplier() { - let docWidth = NumCast(this._document!.width); - let docHeight = NumCast(this._document!.height); - if (NumCast(this._document!.nativeWidth) || !docWidth || !this._panelWidth || !this._panelHeight) return 1; - if (StrCast(this._document!.layout).indexOf("Collection") === -1 || - NumCast(this._document!.viewType) !== CollectionViewType.Freeform) return 1; - let scaling = Math.max(1, this._panelWidth / docWidth * docHeight > this._panelHeight ? - this._panelHeight / docHeight : this._panelWidth / docWidth); - return scaling; - } - get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; } - - addDocTab = (doc: Doc, location: string) => { - if (location === "onRight") { - CollectionDockingView.AddRightSplit(doc); - } else { - CollectionDockingView.AddTab(this._stack, doc); - } - } - get content() { - if (!this._document) { - return (null); - } - return ( -
- -
); - } - - render() { - let theContent = this.content; - return !this._document ? (null) : - { this._panelWidth = r.offset.width; this._panelHeight = r.offset.height; })}> - {({ measureRef }) =>
{theContent}
} -
; - } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 11d71d023..477879b79 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -261,7 +261,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { let dbName = StrCast(this.props.Document.title); let res = await Gateway.Instance.PostSchema(csv, dbName); if (self.props.CollectionView.props.addDocument) { - let schemaDoc = await Docs.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName }, { dbDoc: self.props.Document }); + let schemaDoc = await Docs.Create.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName }, { dbDoc: self.props.Document }); if (schemaDoc) { //self.props.CollectionView.props.addDocument(schemaDoc, false); self.props.Document.schemaDoc = schemaDoc; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index be37efd3d..440a2410b 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -113,20 +113,20 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { protected async getDocumentFromType(type: string, path: string, options: DocumentOptions): Promise> { let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise)) | undefined = undefined; if (type.indexOf("image") !== -1) { - ctor = Docs.ImageDocument; + ctor = Docs.Create.ImageDocument; } if (type.indexOf("video") !== -1) { - ctor = Docs.VideoDocument; + ctor = Docs.Create.VideoDocument; } if (type.indexOf("audio") !== -1) { - ctor = Docs.AudioDocument; + ctor = Docs.Create.AudioDocument; } if (type.indexOf("pdf") !== -1) { - ctor = Docs.PdfDocument; + ctor = Docs.Create.PdfDocument; options.nativeWidth = 1200; } if (type.indexOf("excel") !== -1) { - ctor = Docs.DBDocument; + ctor = Docs.Create.DBDocument; options.dropAction = "copy"; } if (type.indexOf("html") !== -1) { @@ -145,7 +145,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { }); return undefined; } - ctor = Docs.WebDocument; + ctor = Docs.Create.WebDocument; options = { height: options.width, ...options, title: path, nativeWidth: undefined }; } return ctor ? ctor(path, options) : undefined; @@ -175,13 +175,13 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { return; } if (html && html.indexOf(" { onWorkspaceContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.props.document)) }); - ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.KVPDocument(this.props.document, { width: 300, height: 300 }), "onRight"), icon: "layer-group" }); + ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.document, { width: 300, height: 300 }), "onRight"), icon: "layer-group" }); if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) { ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, "inTab"), icon: "folder" }); ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight"), icon: "caret-square-right" }); diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 7853544d5..bd5cd5450 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -98,7 +98,7 @@ export class CollectionVideoView extends React.Component { SearchBox.convertDataUri(dataUrl, filename).then((returnedFilename) => { if (returnedFilename) { let url = DocServer.prepend(returnedFilename); - let imageSummary = Docs.ImageDocument(url, { + let imageSummary = Docs.Create.ImageDocument(url, { x: NumCast(this.props.Document.x) + width, y: NumCast(this.props.Document.y), width: 150, height: height / width * 150, title: "--snapshot" + NumCast(this.props.Document.curPage) + " image-" }); diff --git a/src/client/views/collections/DockedFrameRenderer.tsx b/src/client/views/collections/DockedFrameRenderer.tsx new file mode 100644 index 000000000..25d4b2a49 --- /dev/null +++ b/src/client/views/collections/DockedFrameRenderer.tsx @@ -0,0 +1,116 @@ +import 'golden-layout/src/css/goldenlayout-base.css'; +import 'golden-layout/src/css/goldenlayout-dark-theme.css'; +import { action, observable, reaction, Lambda, IReactionDisposer } from "mobx"; +import { observer } from "mobx-react"; +import Measure, { ContentRect } from "react-measure"; +import { Doc, Field, Opt, DocListCast } from "../../../new_fields/Doc"; +import { FieldId } from "../../../new_fields/RefField"; +import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { emptyFunction, returnTrue, Utils } from "../../../Utils"; +import { DocServer } from "../../DocServer"; +import { Transform } from '../../util/Transform'; +import { DocumentView } from "../nodes/DocumentView"; +import "./CollectionDockingView.scss"; +import { SubCollectionViewProps } from "./CollectionSubView"; +import React = require("react"); +import { CollectionViewType } from './CollectionBaseView'; +import { Id } from '../../../new_fields/FieldSymbols'; +import { CollectionDockingView } from './CollectionDockingView'; + +interface DockedFrameProps { + documentId: FieldId; + glContainer: any; + glEventHub: any; + parent: CollectionDockingView; +} + +@observer +export class DockedFrameRenderer extends React.Component { + _mainCont = React.createRef(); + @observable private _panelWidth = 0; + @observable private _panelHeight = 0; + @observable private _document: Opt; + private get parentProps(): SubCollectionViewProps { + return this.props.parent.props; + } + + get _stack(): any { + let parent = this.props.glContainer.parent.parent; + if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) + return parent.parent.contentItems[1]; + return parent; + } + constructor(props: any) { + super(props); + DocServer.GetRefField(this.props.documentId).then(action((f: Opt) => this._document = f as Doc)); + } + + nativeWidth = () => NumCast(this._document!.nativeWidth, this._panelWidth); + nativeHeight = () => NumCast(this._document!.nativeHeight, this._panelHeight); + contentScaling = () => { + const nativeH = this.nativeHeight(); + const nativeW = this.nativeWidth(); + let wscale = this._panelWidth / nativeW; + return wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale; + } + + ScreenToLocalTransform = () => { + if (this._mainCont.current && this._mainCont.current.children) { + let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current.children[0].firstChild as HTMLElement); + scale = Utils.GetScreenTransform(this._mainCont.current).scale; + return this.parentProps.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale); + } + return Transform.Identity(); + } + get scaleToFitMultiplier() { + let docWidth = NumCast(this._document!.width); + let docHeight = NumCast(this._document!.height); + if (NumCast(this._document!.nativeWidth) || !docWidth || !this._panelWidth || !this._panelHeight) return 1; + if (StrCast(this._document!.layout).indexOf("Collection") === -1 || + NumCast(this._document!.viewType) !== CollectionViewType.Freeform) return 1; + let scaling = Math.max(1, this._panelWidth / docWidth * docHeight > this._panelHeight ? + this._panelHeight / docHeight : this._panelWidth / docWidth); + return scaling; + } + get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; } + + addDocTab = (doc: Doc, location: string) => { + if (location === "onRight") { + CollectionDockingView.AddRightSplit(doc); + } else { + CollectionDockingView.AddTab(this._stack, doc); + } + } + get content() { + if (!this._document) { + return (null); + } + return ( +
+ +
); + } + + render() { + let theContent = this.content; + return !this._document ? (null) : + { this._panelWidth = r.offset.width; this._panelHeight = r.offset.height; })}> + {({ measureRef }) =>
{theContent}
} +
; + } +} \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 29734fa19..cd386abfa 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -79,7 +79,7 @@ export class MarqueeView extends React.Component } ns.map(line => { let indent = line.search(/\S|$/); - let newBox = Docs.TextDocument({ width: 200, height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line }); + let newBox = Docs.Create.TextDocument({ width: 200, height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line }); this.props.addDocument(newBox, false); y += 40 * this.props.getTransform().Scale; }); @@ -89,13 +89,13 @@ export class MarqueeView extends React.Component navigator.clipboard.readText().then(text => { let ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== ""); if (ns.length === 1 && text.startsWith("http")) { - this.props.addDocument(Docs.ImageDocument(text, { nativeWidth: 300, width: 300, x: x, y: y }), false);// paste an image from its URL in the paste buffer + this.props.addDocument(Docs.Create.ImageDocument(text, { nativeWidth: 300, width: 300, x: x, y: y }), false);// paste an image from its URL in the paste buffer } else { this.pasteTable(ns, x, y); } }); } else { - let newBox = Docs.TextDocument({ width: 200, height: 100, x: x, y: y, title: "-typed text-" }); + let newBox = Docs.Create.TextDocument({ width: 200, height: 100, x: x, y: y, title: "-typed text-" }); this.props.addLiveTextDocument(newBox); } e.stopPropagation(); @@ -136,7 +136,7 @@ export class MarqueeView extends React.Component doc.width = 200; docList.push(doc); } - let newCol = Docs.SchemaDocument([...(groupAttr ? ["_group"] : []), ...columns.filter(c => c)], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 }); + let newCol = Docs.Create.SchemaDocument([...(groupAttr ? ["_group"] : []), ...columns.filter(c => c)], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 }); this.props.addDocument(newCol, false); } @@ -259,7 +259,7 @@ export class MarqueeView extends React.Component let ink = Cast(this.props.container.props.Document.ink, InkField); let inkData = ink ? ink.inkData : undefined; let zoomBasis = NumCast(this.props.container.props.Document.scale, 1); - let newCollection = Docs.FreeformDocument(selected, { + let newCollection = Docs.Create.FreeformDocument(selected, { x: bounds.left, y: bounds.top, panX: 0, @@ -283,14 +283,14 @@ export class MarqueeView extends React.Component d.page = -1; return d; }); - let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" }); + let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" }); newCollection.proto!.summaryDoc = summary; selected = [newCollection]; newCollection.x = bounds.left + bounds.width; summary.proto!.subBulletDocs = new List(selected); //summary.proto!.maximizeLocation = "inTab"; // or "inPlace", or "onRight" summary.templates = new List([Templates.Bullet.Layout]); - let container = Docs.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, title: "-summary-" }); + let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, title: "-summary-" }); container.viewType = CollectionViewType.Stacking; this.props.addLiveTextDocument(container); // }); @@ -303,11 +303,11 @@ export class MarqueeView extends React.Component d.page = -1; return d; }); - let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" }); + let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" }); SearchBox.convertDataUri(dataUrl, "icon" + summary[Id] + "_image").then((returnedFilename) => { if (returnedFilename) { let url = DocServer.prepend(returnedFilename); - let imageSummary = Docs.ImageDocument(url, { + let imageSummary = Docs.Create.ImageDocument(url, { x: bounds.left, y: bounds.top + 100 / zoomBasis, width: 150, height: bounds.height / bounds.width * 150, title: "-summary image-" }); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index efba26c2c..16e40000d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -302,7 +302,7 @@ export class DocumentView extends DocComponent(Docu } deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); } - fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight") }; + fieldsClicked = (): void => { this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight") }; makeBtnClicked = (): void => { let doc = Doc.GetProto(this.props.Document); doc.isButton = !BoolCast(doc.isButton, false); @@ -418,7 +418,7 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: "Find aliases", event: async () => { const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document); - this.props.addDocTab && this.props.addDocTab(Docs.SchemaDocument(["title"], aliases, {}), "onRight"); + this.props.addDocTab && this.props.addDocTab(Docs.Create.SchemaDocument(["title"], aliases, {}), "onRight"); }, icon: "search" }); cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document), icon: "crosshairs" }); diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index bfc1738fc..a8f94b746 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -33,7 +33,7 @@ class Uploader extends React.Component { onClick = async () => { try { this.status = "initializing protos"; - await Docs.initProtos(); + await Docs.Prototypes.initialize(); let imgPrev = document.getElementById("img_preview"); if (imgPrev) { let files: FileList | null = inputRef.current!.files; @@ -53,7 +53,7 @@ class Uploader extends React.Component { const json = await res.json(); json.map(async (file: any) => { let path = window.location.origin + file; - var doc = Docs.ImageDocument(path, { nativeWidth: 200, width: 200, title: name }); + var doc = Docs.Create.ImageDocument(path, { nativeWidth: 200, width: 200, title: name }); this.status = "getting user document"; diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 7f7263cf1..af65f5482 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -172,6 +172,18 @@ export namespace Doc { } return protos; } + + /** + * This function is intended to model Object.assign({}, {}) [https://mzl.la/1Mo3l21], which copies + * the values of the properties of a source object into the target. + * + * This is just a specific, Dash-authored version that serves the same role for our + * Doc class. + * + * @param doc the target document into which you'd like to insert the new fields + * @param fields the fields to project onto the target. Its type signature defines a mapping from some string key + * to a potentially undefined field, where each entry in this mapping is optional. + */ export function assign(doc: Doc, fields: Partial>>) { for (const key in fields) { if (fields.hasOwnProperty(key)) { diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index 2b304c373..8caceb063 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -60,6 +60,7 @@ export function getter(target: any, prop: string | symbol | number, receiver: an } return getField(target, prop); } + function getProtoField(protoField: Doc | undefined, prop: string | number, cb?: (field: Field | undefined) => void) { if (!protoField) return undefined; let field = protoField[prop]; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index e5b7a025b..169be3b99 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -33,8 +33,8 @@ export class CurrentUserUtils { doc.title = this.email; doc.data = new List(); doc.excludeFromLibrary = true; - doc.optionalRightCollection = Docs.StackingDocument([], { title: "New mobile uploads" }); - // doc.library = Docs.TreeDocument([doc], { title: `Library: ${CurrentUserUtils.email}` }); + doc.optionalRightCollection = Docs.Create.StackingDocument([], { title: "New mobile uploads" }); + // doc.library = Docs.Create.TreeDocument([doc], { title: `Library: ${CurrentUserUtils.email}` }); // (doc.library as Doc).excludeFromLibrary = true; return doc; } @@ -94,12 +94,12 @@ export class CurrentUserUtils { // new AttributeTransformationModel(atmod, AggregateFunction.None), // new AttributeTransformationModel(atmod, AggregateFunction.Count), // new AttributeTransformationModel(atmod, AggregateFunction.Count)); - // schemaDocuments.push(Docs.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! })); + // schemaDocuments.push(Docs.Create.HistogramDocument(histoOp, { width: 200, height: 200, title: attr.displayName! })); // } // }))); // return promises; // }, [] as Promise[])); - // return CurrentUserUtils._northstarSchemas.push(Docs.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! })); + // return CurrentUserUtils._northstarSchemas.push(Docs.Create.TreeDocument(schemaDocuments, { width: 50, height: 100, title: schema.displayName! })); // }); // } } -- cgit v1.2.3-70-g09d2 From de0304b2966ebdede9d9db8c510e19020046115c Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 17 Jun 2019 13:38:15 -0400 Subject: peripheral renaming fixes --- src/client/documents/Documents.ts | 4 ++-- src/client/util/History.ts | 6 +++--- src/client/util/SearchUtil.ts | 4 ++-- src/client/util/TooltipTextMenu.tsx | 8 ++++---- src/client/views/MainView.tsx | 10 +++++----- src/client/views/SearchBox.tsx | 6 +++--- src/client/views/collections/CollectionDockingView.tsx | 10 +++++----- src/client/views/collections/CollectionSubView.tsx | 8 ++++---- src/client/views/collections/CollectionVideoView.tsx | 2 +- src/client/views/collections/DockedFrameRenderer.tsx | 2 +- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- .../views/collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 6 +++--- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 2 +- src/debug/Viewer.tsx | 2 +- src/mobile/ImageUpload.tsx | 4 ++-- src/new_fields/Proxy.ts | 2 +- src/server/authentication/models/current_user_utils.ts | 6 +++--- 20 files changed, 46 insertions(+), 46 deletions(-) (limited to 'src/client/views/collections/CollectionVideoView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b10954636..758291b9b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -98,7 +98,7 @@ export namespace Docs { // non-guid string ids for each document prototype let protoIds = [textProtoId, histoProtoId, collProtoId, imageProtoId, webProtoId, kvpProtoId, videoProtoId, audioProtoId, pdfProtoId, iconProtoId] // fetch the actual prototype documents from the server - let actualProtos = await DocServer.GetRefFields(protoIds); + let actualProtos = await DocServer.getRefFields(protoIds); // initialize prototype documents textProto = actualProtos[textProtoId] as Doc || CreateTextProto(); @@ -363,7 +363,7 @@ export namespace Docs { CurrentUserUtils.AddNorthstarSchema(schema, schemaDoc); const docs = schemaDocuments; CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => { - DocServer.GetRefField(attr.displayName! + ".alias").then(action((field: Opt) => { + DocServer.getRefField(attr.displayName! + ".alias").then(action((field: Opt) => { if (field instanceof Doc) { docs.push(field); } else { diff --git a/src/client/util/History.ts b/src/client/util/History.ts index 545ea8629..94bfcbe09 100644 --- a/src/client/util/History.ts +++ b/src/client/util/History.ts @@ -88,7 +88,7 @@ export namespace HistoryUtil { } export function createUrl(params: ParsedUrl): string { - let baseUrl = DocServer.prepend(`/${params.type}`); + let baseUrl = DocServer.Util.prepend(`/${params.type}`); switch (params.type) { case "doc": const initializers = encodeURIComponent(JSON.stringify(params.initializers)); @@ -103,7 +103,7 @@ export namespace HistoryUtil { } export async function initDoc(id: string, initializer: DocInitializerList) { - const doc = await DocServer.GetRefField(id); + const doc = await DocServer.getRefField(id); if (!(doc instanceof Doc)) { return; } @@ -111,7 +111,7 @@ export namespace HistoryUtil { } async function onDocUrl(url: DocUrl) { - const field = await DocServer.GetRefField(url.docId); + const field = await DocServer.getRefField(url.docId); await Promise.all(Object.keys(url.initializers).map(id => initDoc(id, url.initializers[id]))); if (field instanceof Doc) { MainView.Instance.openWorkspace(field, true); diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 28ec8ca14..9dd9acbb7 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -7,13 +7,13 @@ export namespace SearchUtil { export function Search(query: string, returnDocs: true): Promise; export function Search(query: string, returnDocs: false): Promise; export async function Search(query: string, returnDocs: boolean) { - const ids = JSON.parse(await rp.get(DocServer.prepend("/search"), { + const ids = JSON.parse(await rp.get(DocServer.Util.prepend("/search"), { qs: { query } })); if (!returnDocs) { return ids; } - const docMap = await DocServer.GetRefFields(ids); + const docMap = await DocServer.getRefFields(ids); return ids.map((id: string) => docMap[id]).filter((doc: any) => doc instanceof Doc); } diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index fa2483db5..36219a99e 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -187,9 +187,9 @@ export class TooltipTextMenu { let link = node && node.marks.find(m => m.type.name === "link"); if (link) { let href: string = link.attrs.href; - if (href.indexOf(DocServer.prepend("/doc/")) === 0) { - let docid = href.replace(DocServer.prepend("/doc/"), ""); - DocServer.GetRefField(docid).then(action((f: Opt) => { + if (href.indexOf(DocServer.Util.prepend("/doc/")) === 0) { + let docid = href.replace(DocServer.Util.prepend("/doc/"), ""); + DocServer.getRefField(docid).then(action((f: Opt) => { if (f instanceof Doc) { if (DocumentManager.Instance.getDocumentView(f)) { DocumentManager.Instance.getDocumentView(f)!.props.focus(f); @@ -218,7 +218,7 @@ export class TooltipTextMenu { handlers: { dragComplete: action(() => { let m = dragData.droppedDocuments; - this.makeLink(DocServer.prepend("/doc/" + m[0][Id])); + this.makeLink(DocServer.Util.prepend("/doc/" + m[0][Id])); }), }, hideSource: false diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 984db0426..734961b56 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -76,11 +76,11 @@ export class MainView extends React.Component { // causes errors to be generated when modifying an observable outside of an action configure({ enforceActions: "observed" }); if (window.location.search.includes("readonly")) { - DocServer.makeReadOnly(); + DocServer.Util.makeReadOnly(); } if (window.location.search.includes("safe")) { if (!window.location.search.includes("nro")) { - DocServer.makeReadOnly(); + DocServer.Util.makeReadOnly(); } CollectionBaseView.SetSafeMode(true); } @@ -141,7 +141,7 @@ export class MainView extends React.Component { this.createNewWorkspace(); } } else { - DocServer.GetRefField(CurrentUserUtils.MainDocId).then(field => + DocServer.getRefField(CurrentUserUtils.MainDocId).then(field => field instanceof Doc ? this.openWorkspace(field) : this.createNewWorkspace(CurrentUserUtils.MainDocId)); } @@ -294,7 +294,7 @@ export class MainView extends React.Component { let logoutRef = React.createRef(); return [ - , + ,
+
]; } diff --git a/src/client/views/SearchBox.tsx b/src/client/views/SearchBox.tsx index 7164d98a4..973715876 100644 --- a/src/client/views/SearchBox.tsx +++ b/src/client/views/SearchBox.tsx @@ -56,13 +56,13 @@ export class SearchBox extends React.Component { @action getResults = async (query: string) => { - let response = await rp.get(DocServer.prepend('/search'), { + let response = await rp.get(DocServer.Util.prepend('/search'), { qs: { query } }); let res: string[] = JSON.parse(response); - const fields = await DocServer.GetRefFields(res); + const fields = await DocServer.getRefFields(res); const docs: Doc[] = []; for (const id of res) { const field = fields[id]; @@ -74,7 +74,7 @@ export class SearchBox extends React.Component { } public static async convertDataUri(imageUri: string, returnedFilename: string) { try { - let posting = DocServer.prepend(RouteStore.dataUriToImage); + let posting = DocServer.Util.prepend(RouteStore.dataUriToImage); const returnedUri = await rp.post(posting, { body: { uri: imageUri, diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index e2bcb10ec..4f5837590 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -306,7 +306,7 @@ export class CollectionDockingView extends React.Component) => + DocServer.getRefField(docid).then(action(async (sourceDoc: Opt) => (sourceDoc instanceof Doc) && DragLinksAsDocuments(tab, x, y, sourceDoc))); } else if ((className === "lm_title" || className === "lm_tab lm_active") && !e.shiftKey) { @@ -320,7 +320,7 @@ export class CollectionDockingView extends React.Component) => { + DocServer.getRefField(docid).then(action((f: Opt) => { if (f instanceof Doc) { DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), x, y, { @@ -372,7 +372,7 @@ export class CollectionDockingView extends React.Component { + DocServer.getRefField(tab.contentItem.config.props.documentId).then(async doc => { if (doc instanceof Doc) { let counter: any = this.htmlToElement(`0`); tab.element.append(counter); @@ -409,7 +409,7 @@ export class CollectionDockingView extends React.Component { - let doc = await DocServer.GetRefField(contentItem.config.props.documentId); + let doc = await DocServer.getRefField(contentItem.config.props.documentId); if (doc instanceof Doc) { let theDoc = doc; CollectionDockingView.TopLevel._removedDocs.push(theDoc); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 440a2410b..36e276d13 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -133,7 +133,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { if (path.includes(window.location.hostname)) { let s = path.split('/'); let id = s[s.length - 1]; - DocServer.GetRefField(id).then(field => { + DocServer.getRefField(id).then(field => { if (field instanceof Doc) { let alias = Doc.MakeAlias(field); alias.x = options.x || 0; @@ -170,8 +170,8 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { if (html && html.indexOf(document.location.origin)) { // prosemirror text containing link to dash document let start = html.indexOf(window.location.origin); let path = html.substr(start, html.length - start); - let docid = path.substr(0, path.indexOf("\">")).replace(DocServer.prepend("/doc/"), "").split("?")[0]; - DocServer.GetRefField(docid).then(f => (f instanceof Doc) && this.props.addDocument(f, false)); + let docid = path.substr(0, path.indexOf("\">")).replace(DocServer.Util.prepend("/doc/"), "").split("?")[0]; + DocServer.getRefField(docid).then(f => (f instanceof Doc) && this.props.addDocument(f, false)); return; } if (html && html.indexOf("(schemaCtor: (doc: Doc) => T) { if (item.kind === "string" && item.type.indexOf("uri") !== -1) { let str: string; let prom = new Promise(resolve => e.dataTransfer.items[i].getAsString(resolve)) - .then(action((s: string) => rp.head(DocServer.prepend(RouteStore.corsProxy + "/" + (str = s))))) + .then(action((s: string) => rp.head(DocServer.Util.prepend(RouteStore.corsProxy + "/" + (str = s))))) .then(result => { let type = result["content-type"]; if (type) { diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index bd5cd5450..ccbac9915 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -97,7 +97,7 @@ export class CollectionVideoView extends React.Component { let filename = encodeURIComponent("snapshot" + this.props.Document.title + "_" + this.props.Document.curPage).replace(/\./g, ""); SearchBox.convertDataUri(dataUrl, filename).then((returnedFilename) => { if (returnedFilename) { - let url = DocServer.prepend(returnedFilename); + let url = DocServer.Util.prepend(returnedFilename); let imageSummary = Docs.Create.ImageDocument(url, { x: NumCast(this.props.Document.x) + width, y: NumCast(this.props.Document.y), width: 150, height: height / width * 150, title: "--snapshot" + NumCast(this.props.Document.curPage) + " image-" diff --git a/src/client/views/collections/DockedFrameRenderer.tsx b/src/client/views/collections/DockedFrameRenderer.tsx index 25d4b2a49..1e7c5661b 100644 --- a/src/client/views/collections/DockedFrameRenderer.tsx +++ b/src/client/views/collections/DockedFrameRenderer.tsx @@ -42,7 +42,7 @@ export class DockedFrameRenderer extends React.Component { } constructor(props: any) { super(props); - DocServer.GetRefField(this.props.documentId).then(action((f: Opt) => this._document = f as Doc)); + DocServer.getRefField(this.props.documentId).then(action((f: Opt) => this._document = f as Doc)); } nativeWidth = () => NumCast(this._document!.nativeWidth, this._panelWidth); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9d19df540..cd613e6ab 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -235,8 +235,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { e.preventDefault(); let start = html.indexOf(window.location.origin); let path = html.substr(start, html.length - start); - let docid = path.substr(0, path.indexOf("\">")).replace(DocServer.prepend("/doc/"), "").split("?")[0]; - DocServer.GetRefField(docid).then(f => { + let docid = path.substr(0, path.indexOf("\">")).replace(DocServer.Util.prepend("/doc/"), "").split("?")[0]; + DocServer.getRefField(docid).then(f => { if (f instanceof Doc) { f.x = pt[0]; f.y = pt[1]; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index cd386abfa..07a58ed64 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -306,7 +306,7 @@ export class MarqueeView extends React.Component let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" }); SearchBox.convertDataUri(dataUrl, "icon" + summary[Id] + "_image").then((returnedFilename) => { if (returnedFilename) { - let url = DocServer.prepend(returnedFilename); + let url = DocServer.Util.prepend(returnedFilename); let imageSummary = Docs.Create.ImageDocument(url, { x: bounds.left, y: bounds.top + 100 / zoomBasis, width: 150, height: bounds.height / bounds.width * 150, title: "-summary image-" diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 16e40000d..fdcb20e9a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -422,7 +422,7 @@ export class DocumentView extends DocComponent(Docu }, icon: "search" }); cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document), icon: "crosshairs" }); - cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])), icon: "link" }); + cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.Util.prepend("/doc/" + this.props.Document[Id])), icon: "link" }); cm.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" }); cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" }); if (!this.topMost) { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index d00a4b928..6a14a04f7 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -237,9 +237,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe href = parent.childNodes[0].href; } if (href) { - if (href.indexOf(DocServer.prepend("/doc/")) === 0) { - let docid = href.replace(DocServer.prepend("/doc/"), "").split("?")[0]; - DocServer.GetRefField(docid).then(f => { + if (href.indexOf(DocServer.Util.prepend("/doc/")) === 0) { + let docid = href.replace(DocServer.Util.prepend("/doc/"), "").split("?")[0]; + DocServer.getRefField(docid).then(f => { (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, document => this.props.addDocTab(document, "inTab")) }); } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index aa29a7170..df9e49b64 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -257,7 +257,7 @@ export class PDFBox extends DocComponent(PdfDocumen .then(action((dataUrl: string) => { SearchBox.convertDataUri(dataUrl, "icon" + this.Document[Id] + "_" + this.curPage).then((returnedFilename) => { if (returnedFilename) { - let url = DocServer.prepend(returnedFilename); + let url = DocServer.Util.prepend(returnedFilename); this.props.Document.thumbnail = new ImageField(new URL(url)); } runInAction(() => this._renderAsSvg = true); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 35ecf12f6..9ab607e91 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -97,7 +97,7 @@ export class VideoBox extends DocComponent(VideoD }; try { let responseSchema: any = {}; - const videoInfoResponse = await rp.get(DocServer.prepend(RouteStore.corsProxy + "/" + `https://www.youtube.com/watch?v=${videoId}`), videoInfoRequestConfig); + const videoInfoResponse = await rp.get(DocServer.Util.prepend(RouteStore.corsProxy + "/" + `https://www.youtube.com/watch?v=${videoId}`), videoInfoRequestConfig); const dataHtml = videoInfoResponse; const start = dataHtml.indexOf('ytplayer.config = ') + 18; const end = dataHtml.indexOf(';ytplayer.load'); diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx index b22300d0b..753149756 100644 --- a/src/debug/Viewer.tsx +++ b/src/debug/Viewer.tsx @@ -146,7 +146,7 @@ class Viewer extends React.Component { @action onKeyPress = (e: React.KeyboardEvent) => { if (e.key === "Enter") { - DocServer.GetRefField(this.idToAdd).then(action((field: any) => { + DocServer.getRefField(this.idToAdd).then(action((field: any) => { if (field !== undefined) { this.fields.push(field); } diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index a8f94b746..df597e0a9 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -57,11 +57,11 @@ class Uploader extends React.Component { this.status = "getting user document"; - const res = await rp.get(DocServer.prepend(RouteStore.getUserDocumentId)); + const res = await rp.get(DocServer.Util.prepend(RouteStore.getUserDocumentId)); if (!res) { throw new Error("No user id returned"); } - const field = await DocServer.GetRefField(res); + const field = await DocServer.getRefField(res); let pending: Opt; if (field instanceof Doc) { pending = await Cast(field.optionalRightCollection, Doc); diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts index 130ec066e..230e4ab8b 100644 --- a/src/new_fields/Proxy.ts +++ b/src/new_fields/Proxy.ts @@ -57,7 +57,7 @@ export class ProxyField extends ObjectField { return undefined; } if (!this.promise) { - this.promise = DocServer.GetRefField(this.fieldId).then(action((field: any) => { + this.promise = DocServer.getRefField(this.fieldId).then(action((field: any) => { this.promise = undefined; this.cache = field; if (field === undefined) this.failed = true; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 169be3b99..95c20d2db 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -40,7 +40,7 @@ export class CurrentUserUtils { } public static async loadCurrentUser(): Promise { - let userPromise = rp.get(DocServer.prepend(RouteStore.getCurrUser)).then(response => { + let userPromise = rp.get(DocServer.Util.prepend(RouteStore.getCurrUser)).then(response => { if (response) { let obj = JSON.parse(response); CurrentUserUtils.curr_id = obj.id as string; @@ -49,9 +49,9 @@ export class CurrentUserUtils { throw new Error("There should be a user! Why does Dash think there isn't one?"); } }); - let userDocPromise = await rp.get(DocServer.prepend(RouteStore.getUserDocumentId)).then(id => { + let userDocPromise = await rp.get(DocServer.Util.prepend(RouteStore.getUserDocumentId)).then(id => { if (id) { - return DocServer.GetRefField(id).then(field => + return DocServer.getRefField(id).then(field => runInAction(() => this.user_document = field instanceof Doc ? field : this.createUserDocument(id))); } else { throw new Error("There should be a user id! Why does Dash think there isn't one?"); -- cgit v1.2.3-70-g09d2 From c619bad0281ea6f248c48b8d418f324f67f530dd Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 3 Jul 2019 12:00:59 -0400 Subject: added to fitToBox to treeviews. fixed some fitToBox issues. moved notifications button to library tree view. --- src/client/northstar/dash-nodes/HistogramBox.tsx | 2 +- .../dash-nodes/HistogramBoxPrimitives.tsx | 2 - src/client/views/Main.scss | 4 +- src/client/views/MainView.tsx | 84 ++++++++-------------- .../views/collections/CollectionSchemaView.tsx | 4 +- .../views/collections/CollectionTreeView.tsx | 38 +++++++++- .../views/collections/CollectionVideoView.tsx | 1 - .../collectionFreeForm/CollectionFreeFormView.tsx | 6 +- src/client/views/nodes/DocumentView.tsx | 5 +- src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/nodes/LinkEditor.tsx | 1 - src/client/views/search/SearchItem.tsx | 26 +++---- src/new_fields/Doc.ts | 12 ++++ 13 files changed, 98 insertions(+), 89 deletions(-) (limited to 'src/client/views/collections/CollectionVideoView.tsx') diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index a60eaea85..b81eafbee 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -36,7 +36,7 @@ export class HistogramBox extends React.Component { @computed public get HistogramResult(): HistogramResult { return this.HistoOp.Result as HistogramResult; } @observable public SizeConverter: SizeConverter = new SizeConverter(); - @computed get createOperationParamsCache() { trace(); return this.HistoOp.CreateOperationParameters(); } + @computed get createOperationParamsCache() { return this.HistoOp.CreateOperationParameters(); } @computed get BinRanges() { return this.HistogramResult ? this.HistogramResult.binRanges : undefined; } @computed get ChartType() { return !this.BinRanges ? ChartType.SinglePoint : this.BinRanges[0] instanceof AggregateBinRange ? diff --git a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx index 350987695..5a16b3782 100644 --- a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx +++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx @@ -62,7 +62,6 @@ export class HistogramBoxPrimitives extends React.Component); } render() { - trace(); return
{this.xaxislines} {this.yaxislines} diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 08f5cb8a5..44c1cb9fc 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -111,8 +111,8 @@ button:hover { //toolbar stuff #toolbar { position: absolute; - bottom: 62px; - left: 24px; + right: 8px; + top: 5px; .toolbar-button { display: block; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 19a04595a..1542fedef 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,44 +1,43 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faArrowDown, faArrowUp, faCheck, faPenNib, faThumbtack, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt, faCut, faExclamation } from '@fortawesome/free-solid-svg-icons'; +import { faArrowDown, faArrowUp, faBell, faCheck, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faThumbtack, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, configure, observable, runInAction, trace } from 'mobx'; +import { action, computed, configure, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; -import { CirclePicker, SliderPicker, BlockPicker, TwitterPicker, SketchPicker } from 'react-color'; import "normalize.css"; import * as React from 'react'; +import { SketchPicker } from 'react-color'; import Measure from 'react-measure'; import * as request from 'request'; +import { Doc, DocListCast, Opt } from '../../new_fields/Doc'; +import { Id } from '../../new_fields/FieldSymbols'; +import { InkTool } from '../../new_fields/InkField'; +import { List } from '../../new_fields/List'; +import { listSpec } from '../../new_fields/Schema'; +import { Cast, FieldValue } from '../../new_fields/Types'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { RouteStore } from '../../server/RouteStore'; -import { emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils'; -import { Docs, DocTypes } from '../documents/Documents'; -import { SetupDrag, DragManager } from '../util/DragManager'; +import { emptyFunction, returnOne, returnTrue } from '../../Utils'; +import { DocServer } from '../DocServer'; +import { Docs } from '../documents/Documents'; +import { SetupDrag } from '../util/DragManager'; +import { HistoryUtil } from '../util/History'; import { Transform } from '../util/Transform'; import { UndoManager } from '../util/UndoManager'; -import { PresentationView } from './presentationview/PresentationView'; +import { CollectionBaseView } from './collections/CollectionBaseView'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { ContextMenu } from './ContextMenu'; import { DocumentDecorations } from './DocumentDecorations'; +import KeyManager from './GlobalKeyHandler'; import { InkingControl } from './InkingControl'; import "./Main.scss"; import { MainOverlayTextBox } from './MainOverlayTextBox'; import { DocumentView } from './nodes/DocumentView'; +import { OverlayView } from './OverlayView'; +import PDFMenu from './pdf/PDFMenu'; +import { PresentationView } from './presentationview/PresentationView'; import { PreviewCursor } from './PreviewCursor'; import { FilterBox } from './search/FilterBox'; -import { SelectionManager } from '../util/SelectionManager'; -import { FieldResult, Field, Doc, Opt, DocListCast } from '../../new_fields/Doc'; -import { Cast, FieldValue, StrCast, PromiseValue } from '../../new_fields/Types'; -import { DocServer } from '../DocServer'; -import { listSpec } from '../../new_fields/Schema'; -import { Id } from '../../new_fields/FieldSymbols'; -import { HistoryUtil } from '../util/History'; -import { CollectionBaseView } from './collections/CollectionBaseView'; -import { List } from '../../new_fields/List'; -import PDFMenu from './pdf/PDFMenu'; -import { InkTool } from '../../new_fields/InkField'; -import _ from "lodash"; -import KeyManager from './GlobalKeyHandler'; -import { OverlayView } from './OverlayView'; +import { CollectionTreeView } from './collections/CollectionTreeView'; @observer export class MainView extends React.Component { @@ -184,8 +183,6 @@ export class MainView extends React.Component { } } - @observable _notifsCol: Opt; - @action openWorkspace = async (doc: Doc, fromHistory = false) => { CurrentUserUtils.MainDocId = doc[Id]; @@ -197,18 +194,12 @@ export class MainView extends React.Component { if (col) { const l = Cast(col.data, listSpec(Doc)); if (l) { - runInAction(() => this._notifsCol = col); + runInAction(() => CollectionTreeView.NotifsCol = col); } } }, 100); } - openNotifsCol = () => { - if (this._notifsCol && CollectionDockingView.Instance) { - CollectionDockingView.Instance.AddRightSplit(this._notifsCol, undefined); - } - } - onDrop = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); @@ -284,24 +275,25 @@ export class MainView extends React.Component { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); } + flyoutWidthFunc = () => this.flyoutWidth; + addDocTabFunc = (doc: Doc) => { + if (doc.dockingConfig) { + this.openWorkspace(doc); + } else { + CollectionDockingView.Instance.AddRightSplit(doc, undefined); + } + }; @computed get flyout() { - let addDocTab = (doc: Doc, dataDoc: Doc | undefined, location: string) => { - if (doc.dockingConfig) { - this.openWorkspace(doc); - } else { - CollectionDockingView.Instance.AddRightSplit(doc, dataDoc); - } - }; return addDocTab(doc, undefined, "onRight")} + addDocTab={this.addDocTabFunc} removeDocument={undefined} ScreenToLocalTransform={Transform.Identity} ContentScaling={returnOne} - PanelWidth={this.getPWidth} + PanelWidth={this.flyoutWidthFunc} PanelHeight={this.getPHeight} renderDepth={0} selectOnLoad={false} @@ -403,23 +395,9 @@ export class MainView extends React.Component { /* @TODO this should really be moved into a moveable toolbar component, but for now let's put it here to meet the deadline */ @computed get miscButtons() { - const length = this._notifsCol ? DocListCast(this._notifsCol.data).length : 0; - const notifsRef = React.createRef(); - const dragNotifs = action(() => this._notifsCol!); let logoutRef = React.createRef(); return [ -
-
- -
0 ? { "display": "initial" } : { "display": "none" }}> - {length} -
-
-
, this.isSearchVisible ?
: null,
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index b0d46953c..d06d1dab6 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -334,7 +334,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @computed get reactTable() { - trace(); let previewWidth = this.previewWidth() + 2 * this.borderWidth + this.DIVIDER_WIDTH + 1; return doc) { } render() { - trace(); return (
this.onDrop(e, {})} onContextMenu={this.onContextMenu} ref={this.createTarget}> @@ -404,6 +402,7 @@ interface CollectionSchemaPreviewProps { Document?: Doc; DataDocument?: Doc; childDocs?: Doc[]; + fitToBox?: () => number[]; renderDepth: number; width: () => number; height: () => number; @@ -472,6 +471,7 @@ export class CollectionSchemaPreview extends React.Component { } ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15); e.stopPropagation(); + e.preventDefault(); } } @@ -312,6 +314,11 @@ class TreeView extends React.Component { return ele; } + fitToBox = () => { + let layoutDoc = Doc.expandTemplateLayout(this.props.document, this.props.dataDoc); + let bounds = Doc.ComputeContentBounds(layoutDoc); + return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Math.min(this.props.panelHeight() / (bounds.b - bounds.y), this.props.panelWidth() / (bounds.r - bounds.x))]; + } render() { let contentElement: (JSX.Element | null) = null; let docList = Cast(this.resolvedDataDoc[this._chosenKey], listSpec(Doc)); @@ -333,6 +340,7 @@ class TreeView extends React.Component { Document={layoutDoc} DataDocument={this.resolvedDataDoc} renderDepth={this.props.renderDepth} + fitToBox={this.fitToBox} width={docWidth} height={layoutDoc[HeightSym]} getTransform={this.docTransform} @@ -456,6 +464,31 @@ export class CollectionTreeView extends CollectionSubView(Document) { outerXf = () => Utils.GetScreenTransform(this._mainEle!); onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {}); + + @observable static NotifsCol: Opt; + + openNotifsCol = () => { + if (CollectionTreeView.NotifsCol && CollectionDockingView.Instance) { + CollectionDockingView.Instance.AddRightSplit(CollectionTreeView.NotifsCol, undefined); + } + } + @computed get notifsButton() { + const length = CollectionTreeView.NotifsCol ? DocListCast(CollectionTreeView.NotifsCol.data).length : 0; + const notifsRef = React.createRef(); + const dragNotifs = action(() => CollectionTreeView.NotifsCol!); + return
+
+ +
0 ? { "display": "initial" } : { "display": "none" }}> + {length} +
+
+
; + } + render() { let dropAction = StrCast(this.props.Document.dropAction) as dropActionType; let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before); @@ -480,6 +513,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { TreeView.loadId = doc[Id]; Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true); }} /> + {this.props.Document.excludeFromLibrary ? this.notifsButton : (null)}
    { TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, addDoc, this.remove, diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index c1a6ca44e..1984965ba 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -122,7 +122,6 @@ export class CollectionVideoView extends React.Component { } render() { - trace(); return ( {this.subView} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 23c0fdd0d..cb7be3f28 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -54,9 +54,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @computed get nativeHeight() { return this.Document.nativeHeight || 0; } public get isAnnotationOverlay() { return this.props.fieldKey === "annotations" || this.props.fieldExt === "annotations"; } private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; } - private panX = () => this.props.fitToBox ? this.props.fitToBox[0] : this.Document.panX || 0; - private panY = () => this.props.fitToBox ? this.props.fitToBox[1] : this.Document.panY || 0; - private zoomScaling = () => this.props.fitToBox ? this.props.fitToBox[2] : this.Document.scale || 1; + private panX = () => this.props.fitToBox ? this.props.fitToBox()[0] : this.Document.panX || 0; + private panY = () => this.props.fitToBox ? this.props.fitToBox()[1] : this.Document.panY || 0; + private zoomScaling = () => this.props.fitToBox ? this.props.fitToBox()[2] : this.Document.scale || 1; private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 : 0; // shift so pan position is at center of window for non-overlay collections private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 : 0;// shift so pan position is at center of window for non-overlay collections private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform()); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 3673cb0e2..8b75d0d25 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -71,7 +71,7 @@ export interface DocumentViewProps { ContainingCollectionView: Opt; Document: Doc; DataDoc?: Doc; - fitToBox?: number[]; + fitToBox?: () => number[]; addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean; removeDocument?: (doc: Doc) => boolean; moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; @@ -573,8 +573,7 @@ export class DocumentView extends DocComponent(Docu @computed get nativeWidth() { return this.Document.nativeWidth || 0; } @computed get nativeHeight() { return this.Document.nativeHeight || 0; } @computed get contents() { - return ( - ); + return (); } render() { diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 3f5a2e744..c5fc6c65a 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -31,7 +31,7 @@ export interface FieldViewProps { fieldKey: string; fieldExt: string; leaveNativeSize?: boolean; - fitToBox?: number[]; + fitToBox?: () => number[]; ContainingCollectionView: Opt; Document: Doc; DataDoc?: Doc; diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 22da732cf..e6cc50620 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -271,7 +271,6 @@ export class LinkGroupEditor extends React.Component { ); } - trace(); return (
    diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 5a7bdd24d..129d71b3b 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -101,34 +101,23 @@ export class SearchItem extends React.Component { @observable _useIcons = true; @observable _displayDim = 50; + fitToBox = () => { + let bounds = Doc.ComputeContentBounds(this.props.doc); + return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Number(SEARCH_THUMBNAIL_SIZE) / Math.max((bounds.b - bounds.y), (bounds.r - bounds.x)), this._displayDim]; + } @computed public get DocumentIcon() { - let layoutresult = StrCast(this.props.doc.type); if (!this._useIcons) { - let renderDoc = this.props.doc; - let box: number[] = []; - if (layoutresult.indexOf(DocTypes.COL) !== -1) { - renderDoc = Doc.MakeDelegate(renderDoc); - let bounds = DocListCast(renderDoc.data).reduce((bounds, doc) => { - var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)]; - let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()]; - return { - x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), - r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) - }; - }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE }); - box = [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Number(SEARCH_THUMBNAIL_SIZE) / (bounds.r - bounds.x), this._displayDim]; - } let returnXDimension = () => this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE); let returnYDimension = () => this._displayDim; - let scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension()); + let scale = () => returnXDimension() / NumCast(this.props.doc.nativeWidth, returnXDimension()); return
    { this._useIcons = !this._useIcons; this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE); })} onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE))} onPointerLeave={action(() => this._displayDim = 50)} > {
    ; } + let layoutresult = StrCast(this.props.doc.type); let button = layoutresult.indexOf(DocTypes.PDF) !== -1 ? faFilePdf : layoutresult.indexOf(DocTypes.IMG) !== -1 ? faImage : layoutresult.indexOf(DocTypes.TEXT) !== -1 ? faStickyNote : diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 734a90731..29d35e19f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -249,6 +249,18 @@ export namespace Doc { return true; } + export function ComputeContentBounds(doc: Doc) { + let bounds = DocListCast(doc.data).reduce((bounds, doc) => { + var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)]; + let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()]; + return { + x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), + r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) + }; + }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE }); + return bounds; + } + // // Resolves a reference to a field by returning 'doc' if o field extension is specified, // otherwise, it returns the extension document stored in doc._ext. -- cgit v1.2.3-70-g09d2 From 0058f56fda4e9e6a70ec7c23e1c141927715f9c1 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 11 Jul 2019 00:38:37 -0400 Subject: trying to make everything work the same way --- src/client/documents/Documents.ts | 8 +++--- src/client/util/LinkManager.ts | 4 +-- src/client/util/TooltipTextMenu.tsx | 2 +- .../views/collections/CollectionBaseView.tsx | 19 ++++++++++--- src/client/views/collections/CollectionPDFView.tsx | 4 +-- .../views/collections/CollectionStackingView.tsx | 10 +++---- src/client/views/collections/CollectionSubView.tsx | 10 ++++--- .../views/collections/CollectionTreeView.tsx | 2 +- .../views/collections/CollectionVideoView.tsx | 4 +-- src/client/views/collections/CollectionView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.scss | 4 +-- .../collectionFreeForm/CollectionFreeFormView.tsx | 33 +++++++++++----------- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/FieldView.tsx | 4 +-- src/client/views/nodes/FormattedTextBox.tsx | 3 +- src/client/views/pdf/PDFViewer.tsx | 2 +- src/new_fields/Doc.ts | 2 +- 17 files changed, 64 insertions(+), 51 deletions(-) (limited to 'src/client/views/collections/CollectionVideoView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d1c9feb32..46255b5d5 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -174,7 +174,7 @@ export namespace Docs { } function CreateImagePrototype(): Doc { - let imageProto = setupPrototypeOptions(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("annotations"), + let imageProto = setupPrototypeOptions(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("data", "annotations"), { x: 0, y: 0, nativeWidth: 600, width: 300, backgroundLayout: ImageBox.LayoutString(), curPage: 0, type: DocTypes.IMG }); return imageProto; } @@ -185,7 +185,7 @@ export namespace Docs { } function CreateHistogramPrototype(): Doc { - let histoProto = setupPrototypeOptions(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("annotations"), + let histoProto = setupPrototypeOptions(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("data"), { x: 0, y: 0, width: 300, height: 300, backgroundColor: "black", backgroundLayout: HistogramBox.LayoutString(), type: DocTypes.HIST }); return histoProto; } @@ -200,7 +200,7 @@ export namespace Docs { return textProto; } function CreatePdfPrototype(): Doc { - let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"), + let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("data", "annotations"), { x: 0, y: 0, width: 300, height: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1, type: DocTypes.PDF }); return pdfProto; } @@ -221,7 +221,7 @@ export namespace Docs { return kvpProto; } function CreateVideoPrototype(): Doc { - let videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionVideoView.LayoutString("annotations"), + let videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionVideoView.LayoutString("data", "annotations"), { x: 0, y: 0, nativeWidth: 600, width: 300, backgroundLayout: VideoBox.LayoutString(), curPage: 0, type: DocTypes.VID }); return videoProto; } diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 944bc532f..d6ea6013b 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -234,9 +234,9 @@ export class LinkManager { //TODO This should also await the return value of the anchor so we don't filter out promises public getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc { if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, null))) { - return Cast(linkDoc.anchor2, Doc, null)!; + return Cast(linkDoc.anchor2, Doc, null); } else { - return Cast(linkDoc.anchor1, Doc, null)!; + return Cast(linkDoc.anchor1, Doc, null); } } } \ No newline at end of file diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index cb7ed976a..4fcc16ddd 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -448,7 +448,7 @@ export class TooltipTextMenu { let node = state.doc.nodeAt(from); node && node.marks.map(m => { m.type === markType && (curLink = m.attrs.href); - }) + }); //toggleMark(markType)(state, dispatch); //return true; } diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index e4f9b5058..079a44b88 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -74,21 +74,27 @@ export class CollectionBaseView extends React.Component { this.props.whenActiveChanged(isActive); } + public get isAnnotationOverlay() { return this.props.fieldKey === "annotations" || this.props.fieldExt === "annotations"; } + @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.isAnnotationOverlay ? "dummy" : ""); } + @action.bound addDocument(doc: Doc, allowDuplicates: boolean = false): boolean { + let self = this; var curPage = NumCast(this.props.Document.curPage, -1); Doc.GetProto(doc).page = curPage; if (curPage >= 0) { Doc.GetProto(doc).annotationOn = this.props.Document; } allowDuplicates = true; - const value = Cast(this.dataDoc[this.dataField], listSpec(Doc)); + let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document; + let targetField = (this.props.fieldExt || this.props.Document.isTemplate) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey; + const value = Cast(targetDataDoc[targetField], listSpec(Doc)); if (value !== undefined) { if (allowDuplicates || !value.some(v => v instanceof Doc && v[Id] === doc[Id])) { value.push(doc); } } else { - Doc.GetProto(this.dataDoc)[this.dataField] = new List([doc]); + Doc.GetProto(targetDataDoc)[targetField] = new List([doc]); } return true; } @@ -98,7 +104,9 @@ export class CollectionBaseView extends React.Component { let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView); docView && SelectionManager.DeselectDoc(docView); //TODO This won't create the field if it doesn't already exist - const value = Cast(this.dataDoc[this.dataField], listSpec(Doc), []); + let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document; + let targetField = (this.props.fieldExt || this.props.Document.isTemplate) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey; + let value = Cast(targetDataDoc[targetField], listSpec(Doc), []); let index = value.reduce((p, v, i) => (v instanceof Doc && v[Id] === doc[Id]) ? i : p, -1); PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn => annotationOn === this.dataDoc.Document && (doc.annotationOn = undefined) @@ -116,7 +124,10 @@ export class CollectionBaseView extends React.Component { @action.bound moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { - if (Doc.AreProtosEqual(this.dataDoc, targetCollection)) { + let self = this; + let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document; + if (Doc.AreProtosEqual(targetDataDoc, targetCollection)) { + //if (Doc.AreProtosEqual(this.extensionDoc, targetCollection)) { return true; } if (this.removeDocument(doc)) { diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 31a73ab36..c97443785 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -47,8 +47,8 @@ export class CollectionPDFView extends React.Component { this._reactionDisposer && this._reactionDisposer(); } - public static LayoutString(fieldKey: string = "data") { - return FieldView.LayoutString(CollectionPDFView, fieldKey); + public static LayoutString(fieldKey: string = "data", fieldExt: string = "annotations") { + return FieldView.LayoutString(CollectionPDFView, fieldKey, fieldExt); } @observable _inThumb = false; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 372891923..9266fc8fd 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -50,11 +50,11 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } overlays = (doc: Doc) => { - return doc.type === DocTypes.IMG ? { title: "title", caption: "caption" } : {} + return doc.type === DocTypes.IMG ? { title: "title", caption: "caption" } : {}; } getDisplayDoc(layoutDoc: Doc, d: Doc, dxf: () => Transform) { - let dataDoc = d !== this.props.DataDoc ? this.props.DataDoc : undefined + let dataDoc = d !== this.props.DataDoc ? this.props.DataDoc : undefined; let width = () => d.nativeWidth ? Math.min(layoutDoc[WidthSym](), this.columnWidth) : this.columnWidth; let height = () => this.getDocHeight(layoutDoc); let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]()); @@ -75,7 +75,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { addDocTab={this.props.addDocTab} setPreviewScript={emptyFunction} previewScript={undefined}> - + ; } getDocHeight(d: Doc) { let nw = NumCast(d.nativeWidth); @@ -176,7 +176,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) { targInd = i; } - }) + }); } if (super.drop(e, de)) { let newDoc = de.data.droppedDocuments[0]; @@ -202,7 +202,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) { targInd = i; } - }) + }); super.onDrop(e, {}, () => { if (targInd !== -1) { let newDoc = this.childDocs[this.childDocs.length - 1]; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 496f7e461..e72e69330 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -46,18 +46,20 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { this.createDropTarget(ele); } - @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); } + public get isAnnotationOverlay() { return this.props.fieldKey === "annotations" || this.props.fieldExt === "annotations"; } + @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.isAnnotationOverlay ? "dummy" : ""); } + get childDocs() { let self = this; //TODO tfs: This might not be what we want? //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue) - return DocListCast((BoolCast(this.props.Document.isTemplate) ? this.extensionDoc : this.props.Document)[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey]); + return DocListCast(this.extensionDoc[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey]); } get childDocList() { //TODO tfs: This might not be what we want? //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue) - return Cast((BoolCast(this.props.Document.isTemplate) ? this.extensionDoc : this.props.Document)[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey], listSpec(Doc)); + return Cast(this.extensionDoc[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey], listSpec(Doc)); } @action @@ -102,7 +104,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { } else if (de.data.moveDocument) { let movedDocs = de.data.options === this.props.Document[Id] ? de.data.draggedDocuments : de.data.droppedDocuments; added = movedDocs.reduce((added: boolean, d) => - de.data.moveDocument(d, this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.addDocument) || added, false); + de.data.moveDocument(d, /*this.props.DataDoc ? this.props.DataDoc :*/ this.props.Document, this.props.addDocument) || added, false); } else { added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false); } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 3674a743a..1280d515c 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -75,7 +75,7 @@ class TreeView extends React.Component { @computed get fieldKey() { let keys = Array.from(Object.keys(this.resolvedDataDoc)); // bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set if (this.resolvedDataDoc.proto instanceof Doc) { - let arr = Array.from(Object.keys(this.resolvedDataDoc.proto!));// bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set + let arr = Array.from(Object.keys(this.resolvedDataDoc.proto));// bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set keys.push(...arr); while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); } diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 1984965ba..16287a658 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -21,8 +21,8 @@ import { Docs, DocUtils } from "../../documents/Documents"; export class CollectionVideoView extends React.Component { private _videoBox?: VideoBox; - public static LayoutString(fieldKey: string = "data") { - return FieldView.LayoutString(CollectionVideoView, fieldKey); + public static LayoutString(fieldKey: string = "data", fieldExt: string = "annotations") { + return FieldView.LayoutString(CollectionVideoView, fieldKey, fieldExt); } private get uIButtons() { let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index e500e5c70..e69696390 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -27,7 +27,7 @@ library.add(faThList); @observer export class CollectionView extends React.Component { - public static LayoutString(fieldStr: string = "data") { return FieldView.LayoutString(CollectionView, fieldStr); } + public static LayoutString(fieldStr: string = "data", fieldExt: string = "") { return FieldView.LayoutString(CollectionView, fieldStr, fieldExt); } private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => { let props = { ...this.props, ...renderProps }; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index ccf261c95..51cfa77b1 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -38,7 +38,7 @@ // } box-shadow: $intermediate-color 0.2vw 0.2vw 0.8vw; border: 0px solid $light-color-secondary; - border-radius: $border-radius; + border-radius: inherit; box-sizing: border-box; position: absolute; @@ -71,7 +71,7 @@ opacity: 0.99; border: 0px solid transparent; - border-radius: $border-radius; + border-radius: inherit; box-sizing: border-box; position:absolute; z-index: -1; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 2c9b6b6a9..c931c74bc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -54,7 +54,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @computed get nativeWidth() { return this.Document.nativeWidth || 0; } @computed get nativeHeight() { return this.Document.nativeHeight || 0; } - public get isAnnotationOverlay() { return this.props.fieldKey === "annotations" || this.props.fieldExt === "annotations"; } + public get isAnnotationOverlay() { return this.props.fieldExt === "annotations"; } private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; } private panX = () => this.props.fitToBox ? this.props.fitToBox()[0] : this.Document.panX || 0; private panY = () => this.props.fitToBox ? this.props.fitToBox()[1] : this.Document.panY || 0; @@ -86,6 +86,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }); } + @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.isAnnotationOverlay ? "dummy" : ""); } + + @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { @@ -93,10 +96,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { if (de.data instanceof DragManager.DocumentDragData) { if (de.data.droppedDocuments.length) { let dragDoc = de.data.droppedDocuments[0]; - let zoom = NumCast(dragDoc.zoomBasis, 1); let [xp, yp] = this.getTransform().transformPoint(de.x, de.y); - let x = xp - de.data.xOffset / zoom; - let y = yp - de.data.yOffset / zoom; + let x = xp - de.data.xOffset; + let y = yp - de.data.yOffset; let dropX = NumCast(de.data.droppedDocuments[0].x); let dropY = NumCast(de.data.droppedDocuments[0].y); de.data.droppedDocuments.forEach(d => { @@ -117,10 +119,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { else if (de.data instanceof DragManager.AnnotationDragData) { if (de.data.dropDocument) { let dragDoc = de.data.dropDocument; - let zoom = NumCast(dragDoc.zoomBasis, 1); let [xp, yp] = this.getTransform().transformPoint(de.x, de.y); - let x = xp - de.data.xOffset / zoom; - let y = yp - de.data.yOffset / zoom; + let x = xp - de.data.xOffset; + let y = yp - de.data.yOffset; let dropX = NumCast(de.data.dropDocument.x); let dropY = NumCast(de.data.dropDocument.y); dragDoc.x = x + NumCast(dragDoc.x) - dropX; @@ -159,14 +160,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { if (!this.isAnnotationOverlay) { PDFMenu.Instance.fadeOut(true); let minx = docs.length ? NumCast(docs[0].x) : 0; - let maxx = docs.length ? NumCast(docs[0].width) / NumCast(docs[0].zoomBasis, 1) + minx : minx; + let maxx = docs.length ? NumCast(docs[0].width) + minx : minx; let miny = docs.length ? NumCast(docs[0].y) : 0; - let maxy = docs.length ? NumCast(docs[0].height) / NumCast(docs[0].zoomBasis, 1) + miny : miny; + let maxy = docs.length ? NumCast(docs[0].height) + miny : miny; let ranges = docs.filter(doc => doc).reduce((range, doc) => { let x = NumCast(doc.x); - let xe = x + NumCast(doc.width) / NumCast(doc.zoomBasis, 1); + let xe = x + NumCast(doc.width); let y = NumCast(doc.y); - let ye = y + NumCast(doc.height) / NumCast(doc.zoomBasis, 1); + let ye = y + NumCast(doc.height); return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]], [range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]]; }, [[minx, maxx], [miny, maxy]]); @@ -299,8 +300,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } SelectionManager.DeselectAll(); - const newPanX = NumCast(doc.x) + NumCast(doc.width) / NumCast(doc.zoomBasis, 1) / 2; - const newPanY = NumCast(doc.y) + NumCast(doc.height) / NumCast(doc.zoomBasis, 1) / 2; + const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2; + const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2; const newState = HistoryUtil.getState(); newState.initializers[id] = { panX: newPanX, panY: newPanY }; HistoryUtil.pushState(newState); @@ -501,19 +502,17 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`; const easing = () => this.props.Document.panTransformType === "Ease"; - if (this.props.fieldExt) Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey); + Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey); return (
    - - + {this.childViews} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6f3adb6b5..613cbf98e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -530,7 +530,7 @@ export class DocumentView extends DocComponent(Docu this.props.addDocument && this.props.addDocument(coll); this.props.removeDocument && this.props.removeDocument(this.props.Document); }, icon: "window-restore" - }) + }); cm.addItem({ description: "Find aliases", event: async () => { const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index c5fc6c65a..abd0d9e12 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -55,8 +55,8 @@ export interface FieldViewProps { @observer export class FieldView extends React.Component { - public static LayoutString(fieldType: { name: string }, fieldStr: string = "data") { - return `<${fieldType.name} {...props} fieldKey={"${fieldStr}"} />`; + public static LayoutString(fieldType: { name: string }, fieldStr: string = "data", fieldExt: string = "") { + return `<${fieldType.name} {...props} fieldKey={"${fieldStr}"} fieldExt={"${fieldExt}"} />`; } @computed diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 5475aa2b2..084fd4c96 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -232,9 +232,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe return field ? field.Data : `{"doc":{"type":"doc","content":[]},"selection":{"type":"text","anchor":0,"head":0}}`; }, field2 => { - if (StrCast(this.props.Document.layout).indexOf("\"" + this.props.fieldKey + "\"") !== -1) // bcz: UGH! why is this needed... something is happening out of order. test with making a collection, then adding a text note and converting that to a template field. + if (StrCast(this.props.Document.layout).indexOf("\"" + this.props.fieldKey + "\"") !== -1) { // bcz: UGH! why is this needed... something is happening out of order. test with making a collection, then adding a text note and converting that to a template field. this._editorView && !this._applyingChange && this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field2))); + } } ); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 8af29110f..8bba05850 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -588,7 +588,7 @@ export class Viewer extends React.Component { } return true; }); - this.Index = Math.min(this.Index + 1, filtered.length - 1) + this.Index = Math.min(this.Index + 1, filtered.length - 1); } nextResult = () => { diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 3d46243e0..cc0dc71dd 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -372,7 +372,7 @@ export namespace Doc { } let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); if (backgroundLayout) { - layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"annotations"}/, `fieldKey={"${metaKey}"} fieldExt={"annotations"}`); + layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"} fieldExt={"annotations"}`); backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); } let nw = Cast(fieldTemplate.nativeWidth, "number"); -- cgit v1.2.3-70-g09d2 From 0c4fe96495ea73c16dcf189b09dbceb44943b8ba Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 12 Jul 2019 18:06:34 -0400 Subject: enabled youtube play controls. fixed youtube video playing . --- src/client/views/collections/CollectionSubView.tsx | 4 +- .../views/collections/CollectionVideoView.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 65 +++++++++++----------- 3 files changed, 34 insertions(+), 37 deletions(-) (limited to 'src/client/views/collections/CollectionVideoView.tsx') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index b2c3fb7d0..71f1908f0 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -197,7 +197,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { .then(result => { let type = result["content-type"]; if (type) { - Docs.Get.DocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 }) + Docs.Get.DocumentFromType(type, str, { ...options, width: 300, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300 }) .then(doc => doc && this.props.addDocument(doc, false)); } }); @@ -218,7 +218,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { body: formData }).then(async (res: Response) => { (await res.json()).map(action((file: any) => { - let full = { ...options, nativeWidth: 300, width: 300, title: dropFileName }; + let full = { ...options, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300, width: 300, title: dropFileName }; let path = DocServer.prepend(file); Docs.Get.DocumentFromType(type, path, full).then(doc => doc && this.props.addDocument(doc)); })); diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 2b6c272aa..8d519ff2b 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -43,7 +43,7 @@ export class CollectionVideoView extends React.Component { @action onPlayDown = () => { - if (this._videoBox && this._videoBox.player) { + if (this._videoBox) { if (this._videoBox.Playing) { this._videoBox.Pause(); } else { diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 9ee4d7942..1b9138cfd 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -47,16 +47,31 @@ export class VideoBox extends DocComponent(VideoD @action public Play() { this.Playing = true; - if (this.player) this.player.play(); - if (!this._playTimer) this._playTimer = setInterval(this.updateTimecode, 500); + if (this.player) { + this.player.play(); + if (!this._playTimer) this._playTimer = setInterval(this.updateTimecode, 500); + } else if (this._youtubePlayer) { + this._youtubePlayer.playVideo(); + if (!this._playTimer) this._playTimer = setInterval(this.updateYoutubeTimecode, 1000); + } } @action public Pause() { this.Playing = false; - if (this.player) this.player.pause(); - if (this._playTimer) { - clearInterval(this._playTimer); - this._playTimer = undefined; + if (this.player) { + this.player.pause(); + if (this._playTimer) { + clearInterval(this._playTimer); + this._playTimer = undefined; + } + } else if (this._youtubePlayer) { + // let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive"; + // this._youtubePlayer.getIframe().style.pointerEvents = interactive ? "all" : "none"; + this._youtubePlayer.pauseVideo(); + if (this._playTimer) { + clearInterval(this._playTimer); + this._playTimer = undefined; + } } } @@ -91,39 +106,24 @@ export class VideoBox extends DocComponent(VideoD height: `${NumCast(this.props.Document.height)}`, width: `${NumCast(this.props.Document.width)}`, videoId: videoid.toString(), - playerVars: { 'controls': 0 }, + playerVars: { 'controls': VideoBox._showControls ? 1 : 0 }, events: { - //'onReady': this.onPlayerReady, + 'onStateChange': this.onPlayerStateChange, } }); // let iframe = $(document.getElementById(`${videoid}-player`)!); // iframe.on("load", function () { // iframe.contents().find("head") // .append($("")); - // }); - reaction(() => this.props.isSelected(), (sel) => { - if (sel) { - this._youtubePlayer.playVideo(); - if (!this._playTimer) this._playTimer = setInterval(this.updateYoutubeTimecode, 1000); - } else { - let iframe = $(document.getElementById(`${videoid}-player`)!); - // .ytp-pause-overlay, .ytp-scroll-min { opacity : 0 !important; } - // $('iframe').load( function() { - // $('iframe').contents().find("head") - // .append($("")); - // }); - //this._youtubePlayer.cueVideoById(videoid, this._youtubePlayer.getCurrentTime()); - this._youtubePlayer.pauseVideo(); - if (this._playTimer) { - clearInterval(this._playTimer); - this._playTimer = undefined; - } - } - }); + // }) } + } - + @action + onPlayerStateChange = (event: any) => { + this.Playing = event.data == YT.PlayerState.PLAYING; } + componentWillUnmount() { this.Pause(); if (this._reactionDisposer) this._reactionDisposer(); @@ -141,10 +141,6 @@ export class VideoBox extends DocComponent(VideoD } } - - onPointerDown = (e: React.PointerEvent) => { - } - @observable static _showControls: boolean = false; specificContextMenu = (e: React.MouseEvent): void => { @@ -162,10 +158,11 @@ export class VideoBox extends DocComponent(VideoD let style = "videoBox-cont" + (this._fullScreen ? "-fullScreen" : interactive); let videoid = field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : ""; + if (this._youtubePlayer) this._youtubePlayer.getIframe().style.pointerEvents = interactive ? "all" : "none"; return !field ?
    Loading
    : videoid ?
    : -
    )} -
  • +