From 0538db262ff611c5273c363470886d2aa42cf760 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 1 Oct 2019 20:31:27 -0400 Subject: initial commit --- src/client/documents/Documents.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 0d04d044e..eed8d61c2 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -254,7 +254,7 @@ export namespace Docs { let title = prototypeId.toUpperCase().replace(upper, `_${upper}`); // synthesize the default options, the type and title from computed values and // whatever options pertain to this specific prototype - let options = { title: title, type: type, baseProto: true, ...defaultOptions, ...(template.options || {}) }; + let options = { title, type, baseProto: true, ...defaultOptions, ...(template.options || {}) }; let primary = layout.view.LayoutString(layout.ext); let collectionView = layout.collectionView; if (collectionView) { -- cgit v1.2.3-70-g09d2 From 9427474b473d70974784a1517a1be902fb8d18ee Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 2 Oct 2019 18:26:55 -0400 Subject: many fixes to pdfs, linking, annotations, presentations. --- src/client/documents/Documents.ts | 24 ++-- src/client/util/DocumentManager.ts | 125 +++++++++++---------- src/client/util/RichTextSchema.tsx | 9 +- src/client/util/SharingManager.tsx | 2 +- .../views/collections/CollectionBaseView.tsx | 9 +- .../views/collections/CollectionDockingView.tsx | 10 +- src/client/views/collections/CollectionSubView.tsx | 1 + .../views/collections/CollectionTreeView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 10 +- src/client/views/linking/LinkFollowBox.tsx | 11 +- src/client/views/nodes/DocumentView.tsx | 44 ++------ src/client/views/nodes/FormattedTextBox.tsx | 79 ++++++------- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 17 ++- src/client/views/nodes/PresBox.tsx | 24 ++-- src/client/views/nodes/VideoBox.tsx | 2 +- src/client/views/pdf/Annotation.tsx | 17 ++- src/client/views/pdf/PDFMenu.tsx | 6 +- src/client/views/pdf/PDFViewer.tsx | 72 +++++------- .../views/presentationview/PresElementBox.tsx | 32 +++--- src/new_fields/Doc.ts | 2 + 21 files changed, 224 insertions(+), 276 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 4d9698532..2d323ea4b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -653,32 +653,30 @@ export namespace DocUtils { } }); } - export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", sourceContext?: Doc, id?: string, anchored1?: boolean) { - let sv = DocumentManager.Instance.getDocumentView(source); - if (sv && sv.props.ContainingCollectionDoc === target) return; - if (target === CurrentUserUtils.UserDocument) return undefined; + export function MakeLink(source: {doc:Doc,ctx?:Doc}, target: {doc:Doc,ctx?:Doc}, title: string = "", description: string = "", id?: string, anchored1?: boolean) { + let sv = DocumentManager.Instance.getDocumentView(source.doc); + if (sv && sv.props.ContainingCollectionDoc === target.doc) return; + if (target.doc === CurrentUserUtils.UserDocument) return undefined; let linkDocProto = new Doc(id, true); UndoManager.RunInBatch(() => { linkDocProto.type = DocumentType.LINK; - linkDocProto.targetContext = targetContext; - linkDocProto.sourceContext = sourceContext; - linkDocProto.title = title === "" ? source.title + " to " + target.title : title; + linkDocProto.targetContext = target.ctx; + linkDocProto.sourceContext = source.ctx; + linkDocProto.title = title === "" ? source.doc.title + " to " + target.doc.title : title; linkDocProto.linkDescription = description; - linkDocProto.anchor1 = source; - linkDocProto.anchor1Page = source.curPage; + linkDocProto.anchor1 = source.doc; linkDocProto.anchor1Groups = new List([]); linkDocProto.anchor1anchored = anchored1; - linkDocProto.anchor2 = target; - linkDocProto.anchor2Page = target.curPage; + linkDocProto.anchor2 = target.doc; linkDocProto.anchor2Groups = new List([]); LinkManager.Instance.addLink(linkDocProto); - Doc.GetProto(source).links = ComputedField.MakeFunction("links(this)"); - Doc.GetProto(target).links = ComputedField.MakeFunction("links(this)"); + Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(this)"); + Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(this)"); }, "make link"); return linkDocProto; } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 4e6968f08..4ebcdf83c 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,7 +1,7 @@ import { action, computed, observable } from 'mobx'; import { Doc, DocListCastAsync } from '../../new_fields/Doc'; import { Id } from '../../new_fields/FieldSymbols'; -import { Cast, NumCast } from '../../new_fields/Types'; +import { Cast, NumCast, StrCast } from '../../new_fields/Types'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { CollectionPDFView } from '../views/collections/CollectionPDFView'; import { CollectionVideoView } from '../views/collections/CollectionVideoView'; @@ -56,9 +56,9 @@ export class DocumentManager { return this.getDocumentViewsById(doc[Id]); } - public getDocumentViewById(id: string, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | null { + public getDocumentViewById(id: string, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | undefined { - let toReturn: DocumentView | null = null; + let toReturn: DocumentView | undefined; let passes = preferredCollection ? [preferredCollection, undefined] : [undefined]; for (let pass of passes) { @@ -81,10 +81,14 @@ export class DocumentManager { return toReturn; } - public getDocumentView(toFind: Doc, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | null { + public getDocumentView(toFind: Doc, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | undefined { return this.getDocumentViewById(toFind[Id], preferredCollection); } + public getFirstDocumentView(toFind: Doc): DocumentView | undefined { + const views = this.getDocumentViews(toFind); + return views.length ? views[0] : undefined; + } public getDocumentViews(toFind: Doc): DocumentView[] { let toReturn: DocumentView[] = []; @@ -127,72 +131,70 @@ export class DocumentManager { return pairs; } - - @undoBatch - public jumpToDocument = async (docDelegate: Doc, willZoom: boolean, forceDockFunc: boolean = false, dockFunc?: (doc: Doc) => void, linkPage?: number, docContext?: Doc): Promise => { - let doc = Doc.GetProto(docDelegate); - const contextDoc = await Cast(doc.annotationOn, Doc); - if (contextDoc) { - contextDoc.scrollY = NumCast(doc.y) - NumCast(contextDoc.height) / 2 * 72 / 96; - } - - let docView: DocumentView | null; - // using forceDockFunc as a flag for splitting linked to doc to the right...can change later if needed - if (!forceDockFunc && (docView = DocumentManager.Instance.getDocumentView(doc))) { - Doc.BrushDoc(docView.props.Document); - if (linkPage !== undefined) docView.props.Document.curPage = linkPage; - UndoManager.RunInBatch(() => docView!.props.focus(docView!.props.Document, willZoom), "focus"); + public jumpToDocument = async (targetDoc: Doc, willZoom: boolean, dockFunc?: (doc: Doc) => void, docContext?: Doc, closeContextIfNotFound: boolean = false): Promise => { + const docView = DocumentManager.Instance.getFirstDocumentView(targetDoc); + const annotatedDoc = await Cast(targetDoc.annotationOn, Doc); + if (docView) { // we have a docView already and aren't forced to create a new one ... just focus on the document. TODO move into view if necessary otherwise just highlight? + annotatedDoc && docView.props.focus(annotatedDoc, false); + docView.props.focus(targetDoc, willZoom); } else { - if (!contextDoc) { - let docs = docContext ? await DocListCastAsync(docContext.data) : undefined; - let found = false; - // bcz: this just searches within the context for the target -- perhaps it should recursively search through all children? - docs && docs.map(d => found = found || Doc.AreProtosEqual(d, docDelegate)); - if (docContext && found) { - let targetContextView: DocumentView | null; - - if (!forceDockFunc && docContext && (targetContextView = DocumentManager.Instance.getDocumentView(docContext))) { - docContext.panTransformType = "Ease"; - targetContextView.props.focus(docDelegate, willZoom); - } else { - (dockFunc || CollectionDockingView.AddRightSplit)(docContext, undefined); - setTimeout(() => { - let dv = DocumentManager.Instance.getDocumentView(docContext); - dv && this.jumpToDocument(docDelegate, willZoom, forceDockFunc, - doc => dv!.props.focus(dv!.props.Document, true, 1, () => dv!.props.addDocTab(doc, undefined, "inPlace")), - linkPage); - }, 1050); - } - } else { - const actualDoc = Doc.MakeAlias(docDelegate); - Doc.BrushDoc(actualDoc); - if (linkPage !== undefined) actualDoc.curPage = linkPage; - (dockFunc || CollectionDockingView.AddRightSplit)(actualDoc, undefined); - } + const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined; + const contextDoc = contextDocs && contextDocs.find(doc => Doc.AreProtosEqual(doc, targetDoc)) ? docContext : undefined; + const targetDocContext = (annotatedDoc ? annotatedDoc : contextDoc); + + if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default + (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc)), undefined); } else { - let contextView: DocumentView | null; - Doc.BrushDoc(docDelegate); - let contextViews = DocumentManager.Instance.getDocumentViews(contextDoc); - if (!forceDockFunc && contextViews.length) { - contextView = contextViews[0]; - SelectionManager.SelectDoc(contextView, false); // force unrendered annotations to be created - contextDoc.panTransformType = "Ease"; + const targetDocContextView = DocumentManager.Instance.getFirstDocumentView(targetDocContext); + if (targetDocContextView) { // we have a context view and aren't forced to create a new one ... focus on the context + targetDocContext.panTransformType = "Ease"; + targetDocContext.scrollY = 0; + targetDocContextView.props.focus(targetDocContextView.props.Document, willZoom); + + // now find the target document within the context setTimeout(() => { - SelectionManager.DeselectDoc(contextView!); - docView = DocumentManager.Instance.getDocumentView(docDelegate); - docView && contextView!.props.focus(contextView!.props.Document, willZoom); - docView && UndoManager.RunInBatch(() => docView!.props.focus(docView!.props.Document, willZoom), "focus"); + const retryDocView = DocumentManager.Instance.getDocumentView(targetDoc); + if (retryDocView) { + retryDocView.props.focus(targetDoc, willZoom); // focus on the target if it now exists in the context + } else { + if (closeContextIfNotFound && targetDocContextView.props.removeDocument) targetDocContextView.props.removeDocument(targetDocContextView.props.Document); + (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc)), undefined); // otherwise create a new view of the target + } }, 0); - } else { - (dockFunc || CollectionDockingView.AddRightSplit)(contextDoc, undefined); + } else { // there's no context view so we need to create one first and try again + targetDocContext.scrollY = 0; + (dockFunc || CollectionDockingView.AddRightSplit)(targetDocContext, undefined); setTimeout(() => { - this.jumpToDocument(docDelegate, willZoom, forceDockFunc, dockFunc, linkPage); - }, 1000); + const foundTargetDocContextView = DocumentManager.Instance.getDocumentView(targetDocContext); + if (foundTargetDocContextView) { // we should always find a target context here.... + this.jumpToDocument(targetDoc, willZoom, dockFunc, undefined, true); // so call jump to doc again and if the doc isn't found, it will be created. + } + }, 2000); // the long timeout gives the context view a chance to create its children. think pdf's which need to be activated to render their annotations. } } } } + public async FollowLink(doc: Doc, focus: (doc: Doc, maxLocation: string) => void, zoom: boolean = false, reverse: boolean = false, currentContext?: Doc) { + let linkDocs = LinkManager.Instance.getAllRelatedLinks(doc); + SelectionManager.DeselectAll(); + let firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc) && !linkDoc.anchor1anchored); + let secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc) && !linkDoc.anchor2anchored); + const firstDocWithoutView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0); + const secondDocWithoutView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0); + let first = firstDocWithoutView ? [firstDocWithoutView] : firstDocs; + let second = secondDocWithoutView ? [secondDocWithoutView] : secondDocs; + let linkFollowDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : second.length ? [second[0].anchor1 as Doc, second[0].anchor2 as Doc] : undefined; + let linkFollowDocContexts = first.length ? [await (first[0].targetContext) as Doc, await (first[0].sourceContext) as Doc] : second.length ? [await (second[0].sourceContext) as Doc, await (second[0].targetContext) as Doc] : [undefined, undefined]; + if (linkFollowDocs && !linkFollowDocs.some(l => l instanceof Promise)) { + let maxLocation = StrCast(linkFollowDocs[0].maximizeLocation, "inTab"); + let targetContext = !Doc.AreProtosEqual(linkFollowDocContexts[reverse ? 1 : 0], currentContext) ? linkFollowDocContexts[reverse ? 1 : 0] : undefined; + DocumentManager.Instance.jumpToDocument(linkFollowDocs[reverse ? 1 : 0], zoom, + // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards + (doc: Doc) => focus(doc, maxLocation), targetContext); + } + } + @action zoomIntoScale = (docDelegate: Doc, scale: number) => { let docView = DocumentManager.Instance.getDocumentView(Doc.GetProto(docDelegate)); @@ -202,8 +204,7 @@ export class DocumentManager { getScaleOfDocView = (docDelegate: Doc) => { let doc = Doc.GetProto(docDelegate); - let docView: DocumentView | null; - docView = DocumentManager.Instance.getDocumentView(doc); + const docView = DocumentManager.Instance.getDocumentView(doc); if (docView) { return docView.props.getScale(); } else { diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 64821d8db..49bd93942 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -627,17 +627,16 @@ export class ImageResizeView { let jumpToDoc = await Cast(linkDoc.anchor2, Doc); if (jumpToDoc) { if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { - - DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((jumpToDoc === linkDoc.anchor2 ? linkDoc.anchor2Page : linkDoc.anchor1Page))); + DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey); return; } } if (targetContext) { - DocumentManager.Instance.jumpToDocument(targetContext, e.ctrlKey, false, document => addDocTab(document, undefined, location ? location : "inTab")); + DocumentManager.Instance.jumpToDocument(targetContext, e.ctrlKey, document => addDocTab(document, undefined, location ? location : "inTab")); } else if (jumpToDoc) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, e.ctrlKey, false, document => addDocTab(document, undefined, location ? location : "inTab")); + DocumentManager.Instance.jumpToDocument(jumpToDoc, e.ctrlKey, document => addDocTab(document, undefined, location ? location : "inTab")); } else { - DocumentManager.Instance.jumpToDocument(linkDoc, e.ctrlKey, false, document => addDocTab(document, undefined, location ? location : "inTab")); + DocumentManager.Instance.jumpToDocument(linkDoc, e.ctrlKey, document => addDocTab(document, undefined, location ? location : "inTab")); } } }); diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index f427e40b1..1cde2aa8e 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -192,7 +192,7 @@ export default class SharingManager extends React.Component<{}> { onClick={() => { let context: Opt; if (this.targetDoc && this.targetDocView && (context = this.targetDocView.props.ContainingCollectionView)) { - DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, undefined, undefined, context.props.Document); + DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, context.props.Document); } }} onPointerEnter={action(() => { diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index e928887e2..38d050e5c 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -5,7 +5,7 @@ import { Doc, DocListCast } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; import { listSpec } from '../../../new_fields/Schema'; -import { BoolCast, Cast, NumCast, PromiseValue, StrCast } from '../../../new_fields/Types'; +import { BoolCast, Cast, NumCast, PromiseValue, StrCast, FieldValue } from '../../../new_fields/Types'; import { DocumentManager } from '../../util/DocumentManager'; import { SelectionManager } from '../../util/SelectionManager'; import { ContextMenu } from '../ContextMenu'; @@ -130,8 +130,11 @@ export class CollectionBaseView extends React.Component { let value = Cast(targetDataDoc[targetField], listSpec(Doc), []); let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1); index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1); - PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn => - annotationOn === this.dataDoc.Document && (doc.annotationOn = undefined)); + PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn => { + if (Doc.AreProtosEqual(annotationOn, FieldValue(Cast(this.dataDoc.extendsDoc, Doc)))) { + Doc.GetProto(doc).annotationOn = undefined; + } + }); if (index !== -1) { value.splice(index, 1); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 98aff41d3..14e513157 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -574,8 +574,8 @@ export class DockedFrameRenderer extends React.Component { let curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc; if (curPres) { let pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" }); - Doc.GetProto(pinDoc).target = doc; - Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.target instanceof Doc) && this.target.title.toString()'); + Doc.GetProto(pinDoc).presentationTargetDoc = doc; + Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title.toString()'); const data = Cast(curPres.data, listSpec(Doc)); if (data) { data.push(pinDoc); @@ -614,8 +614,8 @@ export class DockedFrameRenderer extends React.Component { } } - panelWidth = () => this._document!.ignoreAspect || this._document!.fitWidth ? this._panelWidth : Math.min(this._panelWidth, Math.max(NumCast(this._document!.width), this.nativeWidth())); - panelHeight = () => this._document!.ignoreAspect || this._document!.fitWidth ? this._panelHeight : Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), this.nativeHeight())); + panelWidth = () => this._document && this._document.maxWidth ? Math.min(Math.max(NumCast(this._document.width), NumCast(this._document.nativeWidth)), this._panelWidth) : this._panelWidth; + panelHeight = () => this._panelHeight; nativeWidth = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0; nativeHeight = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0; @@ -644,7 +644,7 @@ export class DockedFrameRenderer extends React.Component { } return Transform.Identity(); } - get previewPanelCenteringOffset() { return this.nativeWidth() && !BoolCast(this._document!.ignoreAspect) ? (this._panelWidth - this.nativeWidth() / this.ScreenToLocalTransform().Scale) / 2 : 0; } + get previewPanelCenteringOffset() { return this.nativeWidth() && !this._document!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; } addDocTab = (doc: Doc, dataDoc: Opt, location: string) => { SelectionManager.DeselectAll(); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 155f2718b..28d1eb384 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -31,6 +31,7 @@ export interface CollectionViewProps extends FieldViewProps { moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; PanelWidth: () => number; PanelHeight: () => number; + VisibleHeight?: () => number; chromeCollapsed: boolean; setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index ffd1f9170..882a0f144 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -217,7 +217,7 @@ class TreeView extends React.Component { if (de.data instanceof DragManager.LinkDragData) { let sourceDoc = de.data.linkSourceDocument; let destDoc = this.props.document; - DocUtils.MakeLink(sourceDoc, destDoc); + DocUtils.MakeLink({doc:sourceDoc}, {doc:destDoc}); e.stopPropagation(); } if (de.data instanceof DragManager.DocumentDragData) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index eb40e0bcb..4bdede48a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -403,8 +403,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { SelectionManager.DeselectAll(); if (this.props.Document.scrollHeight) { let annotOn = Cast(doc.annotationOn, Doc) as Doc; - let offset = annotOn && (NumCast(annotOn.height) / 2 * 72 / 96); - this.props.Document.scrollY = NumCast(doc.y) - offset; + if (!annotOn) { + this.props.focus(doc); + } else { + let contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn.height); + let offset = annotOn && (contextHgt / 2 * 96 / 72); + this.props.Document.scrollY = NumCast(doc.y) - offset; + } } else { const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2; const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2; @@ -416,6 +421,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { this.setPan(newPanX, newPanY); this.Document.panTransformType = "Ease"; + Doc.BrushDoc(this.props.Document); this.props.focus(this.props.Document); willZoom && this.setScaleToZoom(doc, scale); diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index 72fff8e53..53b720a9e 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -171,7 +171,7 @@ export class LinkFollowBox extends React.Component { @undoBatch openFullScreen = () => { if (LinkFollowBox.destinationDoc) { - let view: DocumentView | null = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc); + let view = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc); view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view); } } @@ -250,10 +250,10 @@ export class LinkFollowBox extends React.Component { let dockingFunc = (document: Doc) => { (LinkFollowBox._addDocTab || this.props.addDocTab)(document, undefined, "inTab"); SelectionManager.DeselectAll(); }; if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor2 && targetContext) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, async document => dockingFunc(document), undefined, targetContext); + DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, async document => dockingFunc(document), targetContext); } else if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor1 && sourceContext) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, document => dockingFunc(sourceContext!)); + DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, document => dockingFunc(sourceContext!)); if (LinkFollowBox.sourceDoc && LinkFollowBox.destinationDoc) { if (guid) { let views = DocumentManager.Instance.getDocumentViews(jumpToDoc); @@ -264,12 +264,11 @@ export class LinkFollowBox extends React.Component { } } else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, undefined, undefined, - NumCast((LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor2 ? LinkFollowBox.linkDoc.anchor2Page : LinkFollowBox.linkDoc.anchor1Page))); + DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom); } else { - DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, dockingFunc); + DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, dockingFunc); } this.highlightDoc(); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a903f8fe2..3273abc1d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -206,7 +206,7 @@ export class DocumentView extends DocComponent(Docu buttonClick = async (altKey: boolean, ctrlKey: boolean) => { let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs); let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs); - let linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document); + let linkDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document); let expandedDocs: Doc[] = []; expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs; expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs; @@ -223,28 +223,10 @@ export class DocumentView extends DocComponent(Docu expandedDocs.forEach(maxDoc => (!this.props.addDocTab(maxDoc, undefined, "close") && this.props.addDocTab(maxDoc, undefined, maxLocation))); } } - else if (linkedDocs.length) { - SelectionManager.DeselectAll(); - let first = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor1 as Doc, this.props.Document) && !d.anchor1anchored); - let second = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor2 as Doc, this.props.Document) && !d.anchor2anchored); - let firstUnshown = first.filter(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0); - let secondUnshown = second.filter(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0); - if (firstUnshown.length) first = [firstUnshown[0]]; - if (secondUnshown.length) second = [secondUnshown[0]]; - let linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : second.length ? [second[0].anchor1 as Doc, second[0].anchor1 as Doc] : undefined; - - // @TODO: shouldn't always follow target context - let linkedFwdContextDocs = [first.length ? await (first[0].targetContext) as Doc : undefined, undefined]; - let linkedFwdPage = [first.length ? NumCast(first[0].anchor2Page, undefined) : undefined, undefined]; - - if (linkedFwdDocs && !linkedFwdDocs.some(l => l instanceof Promise)) { - let maxLocation = StrCast(linkedFwdDocs[0].maximizeLocation, "inTab"); - let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionDoc) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined; - DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false, - // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards - doc => this.props.focus(this.props.Document, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation)), - linkedFwdPage[altKey ? 1 : 0], targetContext); - } + else if (linkDocs.length) { + DocumentManager.Instance.FollowLink(this.props.Document, + (doc: Doc, maxLocation: string) => this.props.focus(this.props.Document, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation)), + ctrlKey, altKey, this.props.ContainingCollectionDoc); } } @@ -352,15 +334,9 @@ export class DocumentView extends DocComponent(Docu if (de.data instanceof DragManager.AnnotationDragData) { /// this whole section for handling PDF annotations looks weird. Need to rethink this to make it cleaner e.stopPropagation(); - let sourceDoc = de.data.annotationDocument; - let targetDoc = this.props.Document; - let annotations = await DocListCastAsync(sourceDoc.annotations); - sourceDoc.linkedToDoc = true; - de.data.targetContext = this.props.ContainingCollectionDoc; - targetDoc.targetContext = de.data.targetContext; - annotations && annotations.forEach(anno => anno.target = targetDoc); - - DocUtils.MakeLink(sourceDoc, targetDoc, this.props.ContainingCollectionDoc, `Link from ${StrCast(sourceDoc.title)}`); + (de.data as any).linkedToDoc = true; + + DocUtils.MakeLink({ doc: de.data.annotationDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, `Link from ${StrCast(de.data.annotationDocument.title)}`); } if (de.data instanceof DragManager.DocumentDragData && de.data.applyAsTemplate) { Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document); @@ -371,7 +347,7 @@ export class DocumentView extends DocComponent(Docu // const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true); // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView); de.data.linkSourceDocument !== this.props.Document && - (de.data.linkDocument = DocUtils.MakeLink(de.data.linkSourceDocument, this.props.Document, this.props.ContainingCollectionDoc, undefined, "in-text link being created")); // TODODO this is where in text links get passed + (de.data.linkDocument = DocUtils.MakeLink({ doc: de.data.linkSourceDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, "in-text link being created")); // TODODO this is where in text links get passed } } @@ -407,7 +383,7 @@ export class DocumentView extends DocComponent(Docu let portalID = (this.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, ""); DocServer.GetRefField(portalID).then(existingPortal => { let portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: (this.Document.width || 0) + 10, height: this.Document.height || 0, title: portalID }); - DocUtils.MakeLink(this.props.Document, portal, undefined, portalID); + DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: portal }, portalID, "portal link"); this.Document.isButton = true; }); } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 449f56b16..749886d9a 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -9,7 +9,7 @@ import { Fragment, Node, Node as ProsNode, NodeType, Slice, Mark, ResolvedPos } import { EditorState, Plugin, Transaction, TextSelection, NodeSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { DateField } from '../../../new_fields/DateField'; -import { Doc, DocListCast, Opt, WidthSym } from "../../../new_fields/Doc"; +import { Doc, DocListCast, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc"; import { Copy, Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; import { RichTextField } from "../../../new_fields/RichTextField"; @@ -230,7 +230,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value); DocUtils.Publish(this.dataDoc[key] as Doc, value, this.props.addDocument, this.props.removeDocument); if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; } - else DocUtils.MakeLink(this.dataDoc, this.dataDoc[key] as Doc, undefined, "Ref:" + value, undefined, undefined, id, true); + else DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: this.dataDoc[key] as Doc }, "Ref:" + value, "link to named target", id, true); }); }); }); @@ -342,7 +342,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let model: NodeType = [".mov", ".mp4"].includes(url) ? schema.nodes.video : schema.nodes.image; let pos = this._editorView!.posAtCoords({ left: de.x, top: de.y }); this._editorView!.dispatch(this._editorView!.state.tr.insert(pos!.pos, model.create({ src: url, docid: target[Id] }))); - DocUtils.MakeLink(this.dataDoc, target, undefined, "ImgRef:" + target.title, undefined, undefined, target[Id]); + DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "ImgRef:" + target.title); this.tryUpdateHeight(); e.stopPropagation(); } else if (de.data instanceof DragManager.DocumentDragData) { @@ -667,55 +667,42 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe handlePaste = (view: EditorView, event: Event, slice: Slice): boolean => { let cbe = event as ClipboardEvent; - let docId: string; - let regionId: string; - if (!cbe.clipboardData) { - return false; - } - let linkId: string; - docId = cbe.clipboardData.getData("dash/pdfOrigin"); - regionId = cbe.clipboardData.getData("dash/pdfRegion"); - if (!docId || !regionId) { - return false; - } - - DocServer.GetRefField(docId).then(doc => { - DocServer.GetRefField(regionId).then(region => { - if (!(doc instanceof Doc) || !(region instanceof Doc)) { - return; - } - - let annotations = DocListCast(region.annotations); - annotations.forEach(anno => anno.target = this.props.Document); - let fieldExtDoc = Doc.fieldExtensionDoc(doc, "data"); - let targetAnnotations = DocListCast(fieldExtDoc.annotations); - if (targetAnnotations) { - targetAnnotations.push(region); - fieldExtDoc.annotations = new List(targetAnnotations); - } + const pdfDocId = cbe.clipboardData && cbe.clipboardData.getData("dash/pdfOrigin"); + const pdfRegionId = cbe.clipboardData && cbe.clipboardData.getData("dash/pdfRegion"); + if (pdfDocId && pdfRegionId) { + DocServer.GetRefField(pdfDocId).then(pdfDoc => { + DocServer.GetRefField(pdfRegionId).then(pdfRegion => { + if ((pdfDoc instanceof Doc) && (pdfRegion instanceof Doc)) { + setTimeout(async () => { + let targetAnnotations = await DocListCastAsync(Doc.fieldExtensionDoc(pdfDoc, "data").annotations);// bcz: NO... this assumes the pdf is using its 'data' field. need to have the PDF's view handle updating its own annotations + targetAnnotations && targetAnnotations.push(pdfRegion); + }); - let link = DocUtils.MakeLink(this.props.Document, region, doc); - if (link) { - cbe.clipboardData!.setData("dash/linkDoc", link[Id]); - linkId = link[Id]; - let frag = addMarkToFrag(slice.content, (node: Node) => addLinkMark(node, StrCast(doc.title))); - slice = new Slice(frag, slice.openStart, slice.openEnd); - var tr = view.state.tr.replaceSelection(slice); - view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste")); - } + let link = DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: pdfRegion, ctx: pdfDoc }, "note on " + pdfDoc.title, "pasted PDF link"); + if (link) { + cbe.clipboardData!.setData("dash/linkDoc", link[Id]); + let linkId = link[Id]; + let frag = addMarkToFrag(slice.content, (node: Node) => addLinkMark(node, StrCast(pdfDoc.title), linkId)); + slice = new Slice(frag, slice.openStart, slice.openEnd); + var tr = view.state.tr.replaceSelection(slice); + view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste")); + } + } + }); }); - }); + return true; + } + return false; - return true; function addMarkToFrag(frag: Fragment, marker: (node: Node) => Node) { const nodes: Node[] = []; frag.forEach(node => nodes.push(marker(node))); return Fragment.fromArray(nodes); } - function addLinkMark(node: Node, title: string) { + function addLinkMark(node: Node, title: string, linkId: string) { if (!node.isText) { - const content = addMarkToFrag(node.content, (node: Node) => addLinkMark(node, title)); + const content = addMarkToFrag(node.content, (node: Node) => addLinkMark(node, title, linkId)); return node.copy(content); } const marks = [...node.marks]; @@ -879,16 +866,16 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (jumpToDoc) { if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((jumpToDoc === linkDoc.anchor2 ? linkDoc.anchor2Page : linkDoc.anchor1Page))); + DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey); return; } } if (targetContext && (!jumpToDoc || targetContext !== await jumpToDoc.annotationOn)) { - DocumentManager.Instance.jumpToDocument(jumpToDoc || targetContext, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab"), undefined, targetContext); + DocumentManager.Instance.jumpToDocument(jumpToDoc || targetContext, ctrlKey, document => this.props.addDocTab(document, undefined, location ? location : "inTab"), targetContext); } else if (jumpToDoc) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab")); + DocumentManager.Instance.jumpToDocument(jumpToDoc, ctrlKey, document => this.props.addDocTab(document, undefined, location ? location : "inTab")); } else { - DocumentManager.Instance.jumpToDocument(linkDoc, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab")); + DocumentManager.Instance.jumpToDocument(linkDoc, ctrlKey, document => this.props.addDocTab(document, undefined, location ? location : "inTab")); } } }); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 9e9fe1324..fe4f75cad 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -299,7 +299,7 @@ export class ImageBox extends DocComponent(ImageD let rotation = NumCast(this.dataDoc.rotation) % 180; let realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size; let aspect = realsize.height / realsize.width; - if (layoutdoc.nativeHeight !== 0 && layoutdoc.nativeWidth !== 0 && (Math.abs(NumCast(layoutdoc.height) - realsize.height) > 1 || Math.abs(NumCast(layoutdoc.width) - realsize.width) > 1)) { + if (layoutdoc.nativeHeight !== 0 && layoutdoc.nativeWidth !== 0 && (Math.abs(1 - NumCast(layoutdoc.nativeHeight) / NumCast(layoutdoc.nativeWidth) / (realsize.height / realsize.width)) > 0.1)) { setTimeout(action(() => { layoutdoc.height = layoutdoc[WidthSym]() * aspect; layoutdoc.nativeHeight = realsize.height; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 617b580dd..88cd2cdc4 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -57,11 +57,8 @@ export class PDFBox extends DocComponent(PdfDocumen } loaded = (nw: number, nh: number, np: number) => { this.dataDoc.numPages = np; - if (!this.Document.nativeWidth || !this.Document.nativeHeight || !this.Document.scrollHeight) { - let oldaspect = (this.Document.nativeHeight || 0) / (this.Document.nativeWidth || 1); - this.Document.nativeWidth = nw * 96 / 72; - this.Document.nativeHeight = this.Document.nativeHeight ? nw * 96 / 72 * oldaspect : nh * 96 / 72; - } + this.Document.nativeWidth = nw * 96 / 72; + this.Document.nativeHeight = nh * 96 / 72; !this.Document.fitWidth && !this.Document.ignoreAspect && (this.Document.height = this.Document[WidthSym]() * (nh / nw)); } @@ -177,12 +174,14 @@ export class PDFBox extends DocComponent(PdfDocumen ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" }); } + _initialScale: number | undefined; render() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.active ? "" : "-interactive"); let noPdf = !(pdfUrl instanceof PdfField) || !this._pdf; - if (!noPdf && (this.props.isSelected() || this.props.ScreenToLocalTransform().Scale < 2.5)) this._everActive = true; - return (!this._everActive ? + if (this._initialScale === undefined) this._initialScale = this.props.ScreenToLocalTransform().Scale; + if (this.props.isSelected() || this.props.Document.scrollY !== undefined) this._everActive = true; + return (noPdf || (!this._everActive && this.props.ScreenToLocalTransform().Scale > 2.5) ?
{` ${this.props.Document.title}`} @@ -203,11 +202,11 @@ export class PDFBox extends DocComponent(PdfDocumen setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView} renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth} Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling} - addDocTab={this.props.addDocTab} GoToPage={this.gotoPage} + addDocTab={this.props.addDocTab} GoToPage={this.gotoPage} focus={this.props.focus} pinToPres={this.props.pinToPres} addDocument={this.props.addDocument} ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select} isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged} - fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} startupLive={this.props.ScreenToLocalTransform().Scale < 2.5 ? true : false} /> + fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} startupLive={this._initialScale < 2.5 ? true : false} /> {this.settingsPanel()}
); } diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index f44ca99b9..180ed9032 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -43,8 +43,8 @@ export class PresBox extends React.Component { value.forEach((item, i) => { if (item instanceof Doc && item.type !== DocumentType.PRESELEMENT) { let pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" }); - Doc.GetProto(pinDoc).target = item; - Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.target instanceof Doc) && this.target.title.toString()'); + Doc.GetProto(pinDoc).presentationTargetDoc = item; + Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title.toString()'); value.splice(i, 1, pinDoc); } }); @@ -124,13 +124,13 @@ export class PresBox extends React.Component { this.childDocs.forEach((doc, ind) => { //the order of cases is aligned based on priority if (doc.hideTillShownButton && ind <= index) { - (doc.target as Doc).opacity = 1; + (doc.presentationTargetDoc as Doc).opacity = 1; } if (doc.hideAfterButton && ind < index) { - (doc.target as Doc).opacity = 0; + (doc.presentationTargetDoc as Doc).opacity = 0; } if (doc.fadeButton && ind < index) { - (doc.target as Doc).opacity = 0.5; + (doc.presentationTargetDoc as Doc).opacity = 0.5; } }); } @@ -145,13 +145,13 @@ export class PresBox extends React.Component { //the order of cases is aligned based on priority if (key.hideAfterButton && ind >= index) { - (key.target as Doc).opacity = 1; + (key.presentationTargetDoc as Doc).opacity = 1; } if (key.fadeButton && ind >= index) { - (key.target as Doc).opacity = 1; + (key.presentationTargetDoc as Doc).opacity = 1; } if (key.hideTillShownButton && ind > index) { - (key.target as Doc).opacity = 0; + (key.presentationTargetDoc as Doc).opacity = 0; } }); } @@ -162,7 +162,7 @@ export class PresBox extends React.Component { * te option open, navigates to that element. */ navigateToElement = async (curDoc: Doc, fromDocIndex: number) => { - let fromDoc = this.childDocs[fromDocIndex].target as Doc; + let fromDoc = this.childDocs[fromDocIndex].presentationTargetDoc as Doc; let docToJump = curDoc; let willZoom = false; @@ -190,7 +190,7 @@ export class PresBox extends React.Component { //docToJump stayed same meaning, it was not in the group or was the last element in the group if (docToJump === curDoc) { //checking if curDoc has navigation open - let target = await curDoc.target as Doc; + let target = await curDoc.presentationTargetDoc as Doc; if (curDoc.navButton) { DocumentManager.Instance.jumpToDocument(target, false); } else if (curDoc.showButton) { @@ -210,8 +210,8 @@ export class PresBox extends React.Component { let curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc); //awaiting jump so that new scale can be found, since jumping is async - await DocumentManager.Instance.jumpToDocument(await docToJump.target as Doc, willZoom); - let newScale = DocumentManager.Instance.getScaleOfDocView(await curDoc.target as Doc); + await DocumentManager.Instance.jumpToDocument(await docToJump.presentationTargetDoc as Doc, willZoom); + let newScale = DocumentManager.Instance.getScaleOfDocView(await curDoc.presentationTargetDoc as Doc); curDoc.viewScale = newScale; //saving the scale that user was on if (curScale !== 1) { diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index b7d9a1eab..573197117 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -129,7 +129,7 @@ export class VideoBox extends DocComponent(VideoD width: 150, height: height / width * 150, title: "--snapshot" + NumCast(this.props.Document.curPage) + " image-" }); this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.addDocument && this.props.ContainingCollectionView.props.addDocument(imageSummary, false); - DocUtils.MakeLink(imageSummary, this.props.Document); + DocUtils.MakeLink({doc:imageSummary}, {doc: this.props.Document}, "snapshot from " + this.props.Document.title, "video frame snapshot"); } }); } diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 3ed85f6a5..f7f52b3ef 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -1,7 +1,7 @@ import React = require("react"); import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, HeightSym, WidthSym, Opt } from "../../../new_fields/Doc"; +import { Doc, DocListCast, HeightSym, WidthSym, Opt, DocListCastAsync } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; @@ -14,6 +14,7 @@ interface IAnnotationProps { fieldExtensionDoc: Doc; addDocTab: (document: Doc, dataDoc: Opt, where: string) => boolean; pinToPres: (document: Doc) => void; + focus: (doc: Doc) => void; } export default class Annotation extends React.Component { @@ -60,6 +61,7 @@ class RegionAnnotation extends React.Component { } componentWillUnmount() { + this._brushDisposer && this._brushDisposer(); this._reactionDisposer && this._reactionDisposer(); } @@ -94,14 +96,11 @@ class RegionAnnotation extends React.Component { PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true); } else if (e.button === 0) { - let targetDoc = await Cast(this.props.document.target, Doc); - if (targetDoc) { - let context = await Cast(targetDoc.targetContext, Doc); - if (context) { - DocumentManager.Instance.jumpToDocument(targetDoc, false, false, - ((doc) => this.props.addDocTab(targetDoc!, undefined, e.ctrlKey ? "onRight" : "inTab")), - undefined, undefined); - } + let annoGroup = await Cast(this.props.document.group, Doc); + if (annoGroup) { + DocumentManager.Instance.FollowLink(annoGroup, + (doc: Doc, maxLocation: string) => this.props.addDocTab(doc, undefined, e.ctrlKey ? "onRight" : "inTab"), + false, false, undefined); } } } diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 2202351ee..e62542014 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -31,7 +31,7 @@ export default class PDFMenu extends React.Component { @observable public Pinned: boolean = false; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = emptyFunction; - public Highlight: (d: Doc | undefined, color: string) => void = emptyFunction; + public Highlight: (color: string) => void = emptyFunction; public Delete: () => void = emptyFunction; public Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = emptyFunction; public AddTag: (key: string, value: string) => boolean = returnFalse; @@ -156,11 +156,11 @@ export default class PDFMenu extends React.Component { @action highlightClicked = (e: React.MouseEvent) => { if (!this.Pinned) { - this.Highlight(undefined, "#f4f442"); + this.Highlight("#f4f442"); } else { this.Highlighting = !this.Highlighting; - this.Highlight(undefined, "#f4f442"); + this.Highlight("#f4f442"); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 33226eac4..20dfc4d8c 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -10,7 +10,6 @@ import { listSpec } from "../../../new_fields/Schema"; import { ScriptField } from "../../../new_fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import smoothScroll, { Utils, emptyFunction, returnOne } from "../../../Utils"; -import { DocServer } from "../../DocServer"; import { Docs, DocUtils } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { CompiledScript, CompileScript } from "../../util/Scripting"; @@ -44,6 +43,7 @@ interface IViewerProps { select: (isCtrlPressed: boolean) => void; startupLive: boolean; renderDepth: number; + focus: (doc: Doc) => void; isSelected: () => boolean; loaded: (nw: number, nh: number, np: number) => void; active: () => boolean; @@ -108,7 +108,8 @@ export class PDFViewer extends React.Component { // file address of the pdf this._coverPath = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${NumCast(this.props.Document.curPage, 1)}.PNG`))); runInAction(() => this._showWaiting = this._showCover = true); - this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => this.props.isSelected() && SelectionManager.SelectedDocuments().length === 1 && this.setupPdfJsViewer(), { fireImmediately: this.props.startupLive }); + this.props.startupLive && this.setupPdfJsViewer(); + this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => (this.props.isSelected() && SelectionManager.SelectedDocuments().length === 1) && this.setupPdfJsViewer(), { fireImmediately: true }); this._reactionDisposer = reaction( () => this.props.Document.scrollY, (scrollY) => { @@ -136,19 +137,11 @@ export class PDFViewer extends React.Component { if (this.props.active() && e.clipboardData) { e.clipboardData.setData("text/plain", this._selectionText); e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]); - e.clipboardData.setData("dash/pdfRegion", this.makeAnnotationDocument(undefined, "#0390fc")[Id]); + e.clipboardData.setData("dash/pdfRegion", this.makeAnnotationDocument("#0390fc")[Id]); e.preventDefault(); } } - paste = (e: ClipboardEvent) => { - if (e.clipboardData && e.clipboardData.getData("dash/pdfOrigin") === this.props.Document[Id]) { - let linkDocId = e.clipboardData.getData("dash/linkDoc"); - linkDocId && DocServer.GetRefField(linkDocId).then(async (link) => - (link instanceof Doc) && (Doc.GetProto(link).anchor2 = this.makeAnnotationDocument(await Cast(Doc.GetProto(link), Doc), "#0390fc", false))); - } - } - setSelectionText = (text: string) => this._selectionText = text; @action @@ -194,13 +187,6 @@ export class PDFViewer extends React.Component { { fireImmediately: true } ); - document.removeEventListener("copy", this.copy); - document.addEventListener("copy", this.copy); - document.addEventListener("pagesinit", action(() => { - this.pdfViewer.currentScaleValue = this._zoomed = 1; - this.gotoPage(NumCast(this.props.Document.curPage, 1)); - })); - document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false)); this.createPdfViewer(); } @@ -212,6 +198,13 @@ export class PDFViewer extends React.Component { } return; } + document.removeEventListener("copy", this.copy); + document.addEventListener("copy", this.copy); + document.addEventListener("pagesinit", action(() => { + this.pdfViewer.currentScaleValue = this._zoomed = 1; + this.gotoPage(NumCast(this.props.Document.curPage, 1)); + })); + document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false)); var pdfLinkService = new PDFJSViewer.PDFLinkService(); let pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, @@ -229,19 +222,18 @@ export class PDFViewer extends React.Component { } @action - makeAnnotationDocument = (sourceDoc: Doc | undefined, color: string, createLink: boolean = true): Doc => { + makeAnnotationDocument = (color: string): Doc => { let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {}); let mainAnnoDocProto = Doc.GetProto(mainAnnoDoc); let annoDocs: Doc[] = []; let minY = Number.MAX_VALUE; - if (this._savedAnnotations.size() === 1 && this._savedAnnotations.values()[0].length === 1 && !createLink) { + if (this._savedAnnotations.size() === 1 && this._savedAnnotations.values()[0].length === 1) { let anno = this._savedAnnotations.values()[0][0]; let annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: "rgba(255, 0, 0, 0.1)", title: "Annotation on " + StrCast(this.props.Document.title) }); if (anno.style.left) annoDoc.x = parseInt(anno.style.left); if (anno.style.top) annoDoc.y = parseInt(anno.style.top); if (anno.style.height) annoDoc.height = parseInt(anno.style.height); if (anno.style.width) annoDoc.width = parseInt(anno.style.width); - annoDoc.target = sourceDoc; annoDoc.group = mainAnnoDoc; annoDoc.color = color; annoDoc.type = AnnotationTypes.Region; @@ -258,7 +250,6 @@ export class PDFViewer extends React.Component { if (anno.style.top) annoDoc.y = parseInt(anno.style.top); if (anno.style.height) annoDoc.height = parseInt(anno.style.height); if (anno.style.width) annoDoc.width = parseInt(anno.style.width); - annoDoc.target = sourceDoc; annoDoc.group = mainAnnoDoc; annoDoc.color = color; annoDoc.type = AnnotationTypes.Region; @@ -272,9 +263,6 @@ export class PDFViewer extends React.Component { } mainAnnoDocProto.title = "Annotation on " + StrCast(this.props.Document.title); mainAnnoDocProto.annotationOn = this.props.Document; - if (sourceDoc && createLink) { - DocUtils.MakeLink(sourceDoc, mainAnnoDocProto, undefined, `Annotation from ${StrCast(this.props.Document.title)}`); - } this._savedAnnotations.clear(); this.Index = -1; return mainAnnoDoc; @@ -529,7 +517,7 @@ export class PDFViewer extends React.Component { } if (PDFMenu.Instance.Highlighting) { - this.highlight(undefined, "goldenrod"); + this.highlight("goldenrod"); } else { PDFMenu.Instance.StartDrag = this.startDrag; @@ -540,9 +528,9 @@ export class PDFViewer extends React.Component { } @action - highlight = (targetDoc: Doc | undefined, color: string) => { + highlight = (color: string) => { // creates annotation documents for current highlights - let annotationDoc = this.makeAnnotationDocument(targetDoc, color, false); + let annotationDoc = this.makeAnnotationDocument(color); Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, annotationDoc); return annotationDoc; } @@ -555,20 +543,14 @@ export class PDFViewer extends React.Component { startDrag = (e: PointerEvent, ele: HTMLElement): void => { e.preventDefault(); e.stopPropagation(); - let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "New Annotation" }); - targetDoc.targetPage = this.getPageFromScroll(this._marqueeY); - let annotationDoc = this.highlight(undefined, "red"); - annotationDoc.linkedToDoc = false; + let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "Note linked to " + this.props.Document.title }); + let annotationDoc = this.highlight("red"); let dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc); DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, { handlers: { - dragComplete: () => { - if (!annotationDoc.linkedToDoc) { - let annotations = DocListCast(annotationDoc.annotations); - annotations && annotations.forEach(anno => anno.target = targetDoc); - DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(this.props.Document.title)}`); - } - } + dragComplete: () => !(dragData as any).linkedToDoc && + DocUtils.MakeLink({ doc: annotationDoc }, { doc: dragData.dropDocument, ctx: dragData.targetContext }, `Annotation from ${StrCast(this.props.Document.title)}`, "link from PDF") + }, hideSource: false }); @@ -602,6 +584,7 @@ export class PDFViewer extends React.Component { @action.bound removeDocument(doc: Doc): boolean { + Doc.GetProto(doc).annotationOn = undefined; //TODO This won't create the field if it doesn't already exist let targetDataDoc = this.props.fieldExtensionDoc; let targetField = this.props.fieldExt; @@ -634,10 +617,10 @@ export class PDFViewer extends React.Component { getCoverImage = () => { if (!this.props.Document[HeightSym]() || !this.props.Document.nativeHeight) { - setTimeout(() => { + setTimeout((() => { this.props.Document.height = this.props.Document[WidthSym]() * this._coverPath.height / this._coverPath.width; this.props.Document.nativeHeight = nativeWidth * this._coverPath.height / this._coverPath.width; - }, 0); + }).bind(this), 0); } let nativeWidth = NumCast(this.props.Document.nativeWidth); let nativeHeight = NumCast(this.props.Document.nativeHeight); @@ -659,7 +642,7 @@ export class PDFViewer extends React.Component { @computed get annotationLayer() { return
{this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => - )} + )}
; } @computed get pdfViewerDiv() { @@ -686,7 +669,8 @@ export class PDFViewer extends React.Component { setPreviewCursor={this.setPreviewCursor} PanelHeight={() => NumCast(this.props.Document.scrollHeight, NumCast(this.props.Document.nativeHeight))} PanelWidth={() => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : NumCast(this.props.Document.nativeWidth)} - focus={emptyFunction} + VisibleHeight={() => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96} + focus={this.props.focus} isSelected={this.props.isSelected} select={emptyFunction} active={this.active} @@ -694,7 +678,7 @@ export class PDFViewer extends React.Component { whenActiveChanged={this.whenActiveChanged} removeDocument={this.removeDocument} moveDocument={this.moveDocument} - addDocument={(doc: Doc, allow: boolean | undefined) => { Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); return true; }} + addDocument={(doc: Doc, allow: boolean | undefined) => { Doc.GetProto(doc).annotationOn = this.props.Document; Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); return true; }} CollectionView={this.props.ContainingCollectionView} ScreenToLocalTransform={this.scrollXf} ruleProvider={undefined} diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index de3242d32..daf000dc7 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -63,13 +63,11 @@ export class PresElementBox extends React.Component { this.hideTillShownButton = !this.hideTillShownButton; if (!this.hideTillShownButton) { if (this.myIndex >= this.currentIndex) { - (this.props.Document.target as Doc).opacity = 1; + (this.props.Document.presentationTargetDoc as Doc).opacity = 1; } } else { - if (this.presentationDoc.presStatus) { - if (this.myIndex > this.currentIndex) { - (this.props.Document.target as Doc).opacity = 0; - } + if (this.presentationDoc.presStatus && this.myIndex > this.currentIndex) { + (this.props.Document.presentationTargetDoc as Doc).opacity = 0; } } } @@ -85,14 +83,12 @@ export class PresElementBox extends React.Component { this.hideAfterButton = !this.hideAfterButton; if (!this.hideAfterButton) { if (this.myIndex <= this.currentIndex) { - (this.props.Document.target as Doc).opacity = 1; + (this.props.Document.presentationTargetDoc as Doc).opacity = 1; } } else { if (this.fadeButton) this.fadeButton = false; - if (this.presentationDoc.presStatus) { - if (this.myIndex < this.currentIndex) { - (this.props.Document.target as Doc).opacity = 0; - } + if (this.presentationDoc.presStatus && this.myIndex < this.currentIndex) { + (this.props.Document.presentationTargetDoc as Doc).opacity = 0; } } } @@ -108,14 +104,12 @@ export class PresElementBox extends React.Component { this.fadeButton = !this.fadeButton; if (!this.fadeButton) { if (this.myIndex <= this.currentIndex) { - (this.props.Document.target as Doc).opacity = 1; + (this.props.Document.presentationTargetDoc as Doc).opacity = 1; } } else { this.hideAfterButton = false; - if (this.presentationDoc.presStatus) { - if (this.myIndex < this.currentIndex) { - (this.props.Document.target as Doc).opacity = 0.5; - } + if (this.presentationDoc.presStatus && (this.myIndex < this.currentIndex)) { + (this.props.Document.presentationTargetDoc as Doc).opacity = 0.5; } } } @@ -162,7 +156,7 @@ export class PresElementBox extends React.Component { * presentation element. */ renderEmbeddedInline = () => { - if (!this.embedOpen || !(this.props.Document.target instanceof Doc)) { + if (!this.embedOpen || !(this.props.Document.presentationTargetDoc instanceof Doc)) { return (null); } @@ -175,8 +169,8 @@ export class PresElementBox extends React.Component { width: propDocWidth === 0 ? "auto" : propDocWidth * scale(), }}> { let pbi = "presElementBox-interaction"; return (
{ p.focus(p.Document); e.stopPropagation(); }}> {treecontainer ? (null) : <> diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index aeffc81c4..58304cebb 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -665,10 +665,12 @@ export namespace Doc { export function BrushDoc(doc: Doc) { brushManager.BrushedDoc.set(doc, true); brushManager.BrushedDoc.set(Doc.GetDataDoc(doc), true); + return doc; } export function UnBrushDoc(doc: Doc) { brushManager.BrushedDoc.delete(doc); brushManager.BrushedDoc.delete(Doc.GetDataDoc(doc)); + return doc; } export class HighlightBrush { -- cgit v1.2.3-70-g09d2 From 48ac0b54cfe29b97a7add72b2369bfc2896f98f7 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 3 Oct 2019 17:07:33 -0400 Subject: partially fixed editing text boxes in stacking views. tweaked link following from text boxes. --- src/client/documents/Documents.ts | 3 +-- src/client/util/DocumentManager.ts | 4 ++-- src/client/views/MainOverlayTextBox.tsx | 3 ++- src/client/views/nodes/FormattedTextBox.tsx | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2d323ea4b..71b9038d4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -653,7 +653,7 @@ export namespace DocUtils { } }); } - export function MakeLink(source: {doc:Doc,ctx?:Doc}, target: {doc:Doc,ctx?:Doc}, title: string = "", description: string = "", id?: string, anchored1?: boolean) { + export function MakeLink(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, title: string = "", description: string = "", id?: string) { let sv = DocumentManager.Instance.getDocumentView(source.doc); if (sv && sv.props.ContainingCollectionDoc === target.doc) return; if (target.doc === CurrentUserUtils.UserDocument) return undefined; @@ -669,7 +669,6 @@ export namespace DocUtils { linkDocProto.anchor1 = source.doc; linkDocProto.anchor1Groups = new List([]); - linkDocProto.anchor1anchored = anchored1; linkDocProto.anchor2 = target.doc; linkDocProto.anchor2Groups = new List([]); diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 5130db131..ffd311665 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -188,8 +188,8 @@ export class DocumentManager { public async FollowLink(link: Doc | undefined, doc: Doc, focus: (doc: Doc, maxLocation: string) => void, zoom: boolean = false, reverse: boolean = false, currentContext?: Doc) { const linkDocs = link ? [link] : LinkManager.Instance.getAllRelatedLinks(doc); SelectionManager.DeselectAll(); - const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc) && !linkDoc.anchor1anchored); - const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc) && !linkDoc.anchor2anchored); + const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc)); + const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc)); const firstDocWithoutView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0); const secondDocWithoutView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0); const first = firstDocWithoutView ? [firstDocWithoutView] : firstDocs; diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 335cc609f..73eef9478 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -78,7 +78,8 @@ export class MainOverlayTextBox extends React.Component this._textTargetDiv = div; this._textHideOnLeave = FormattedTextBox.InputBoxOverlay && FormattedTextBox.InputBoxOverlay.props.hideOnLeave; if (div) { - this._textBottom = div.parentElement && getComputedStyle(div.parentElement).top !== "0px" ? true : false; + let parTop = div.parentElement && getComputedStyle(div.parentElement).top; + this._textBottom = parTop && parTop !== "0px" && parTop != "auto" ? true : false; this._textColor = (getComputedStyle(div) as any).color; div.style.color = "transparent"; } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index c37258f50..f84301c10 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -184,7 +184,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value); DocUtils.Publish(this.dataDoc[key] as Doc, value, this.props.addDocument, this.props.removeDocument); if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; } - else DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: this.dataDoc[key] as Doc }, "Ref:" + value, "link to named target", id, true); + else DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: this.dataDoc[key] as Doc }, "Ref:" + value, "link to named target", id); }); }); }); @@ -307,7 +307,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new RichTextField(draggedDoc.data.Data); e.stopPropagation(); } - } else { + } else if (de.mods === "CtrlKey") { draggedDoc.isTemplate = true; if (typeof (draggedDoc.layout) === "string") { let layoutDelegateToOverrideFieldKey = Doc.MakeDelegate(draggedDoc); -- cgit v1.2.3-70-g09d2 From f9916faa215297d434aab2357b98d2c4b1fdcb92 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 5 Oct 2019 13:02:43 -0400 Subject: started cleaning up videos. restored link follwing to timecodes. changed to currentTimecode from curPage --- src/client/documents/Documents.ts | 11 +++-- src/client/util/DictationManager.ts | 1 - src/client/util/DocumentManager.ts | 7 ++- src/client/views/InkingCanvas.tsx | 6 +-- .../views/collections/CollectionBaseView.tsx | 4 +- .../views/collections/CollectionSchemaView.tsx | 2 +- .../views/collections/CollectionVideoView.tsx | 6 +-- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/linking/LinkFollowBox.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 6 +-- src/client/views/nodes/VideoBox.tsx | 57 ++++++++++++---------- src/new_fields/InkField.ts | 2 +- 13 files changed, 58 insertions(+), 50 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 71b9038d4..98f56af01 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -74,6 +74,7 @@ export interface DocumentOptions { backgroundLayout?: string; chromeStatus?: string; curPage?: number; + currentTimecode?: number; documentText?: string; borderRounding?: string; schemaColumns?: List; @@ -120,7 +121,7 @@ export namespace Docs { }], [DocumentType.IMG, { layout: { view: ImageBox, collectionView: [CollectionView, data, anno] as CollectionViewType }, - options: { curPage: 0 } + options: {} }], [DocumentType.WEB, { layout: { view: WebBox, collectionView: [CollectionView, data, anno] as CollectionViewType }, @@ -136,7 +137,7 @@ export namespace Docs { }], [DocumentType.VID, { layout: { view: VideoBox, collectionView: [CollectionVideoView, data, anno] as CollectionViewType }, - options: { curPage: 0 }, + options: { currentTimecode: 0 }, }], [DocumentType.AUDIO, { layout: { view: AudioBox }, @@ -662,15 +663,17 @@ export namespace DocUtils { UndoManager.RunInBatch(() => { linkDocProto.type = DocumentType.LINK; - linkDocProto.targetContext = target.ctx; - linkDocProto.sourceContext = source.ctx; linkDocProto.title = title === "" ? source.doc.title + " to " + target.doc.title : title; linkDocProto.linkDescription = description; linkDocProto.anchor1 = source.doc; + linkDocProto.anchor1Context = source.ctx; + linkDocProto.anchor1Timecode = source.doc.currentTimecode; linkDocProto.anchor1Groups = new List([]); linkDocProto.anchor2 = target.doc; + linkDocProto.anchor2Context = target.ctx; linkDocProto.anchor2Groups = new List([]); + linkDocProto.anchor2Timecode = target.doc.currentTimecode; LinkManager.Instance.addLink(linkDocProto); diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index cebb56bbe..3b2307073 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -336,7 +336,6 @@ export namespace DictationManager { let newBox = Docs.Create.TextDocument({ width: 400, height: 200, title: "My Outline" }); newBox.autoHeight = true; let proto = newBox.proto!; - proto.page = -1; let prompt = "Press alt + r to start dictating here..."; let head = 3; let anchor = head + prompt.length; diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 6fe97edbb..83a69465f 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,4 +1,4 @@ -import { action, computed, observable } from 'mobx'; +import { action, computed, observable, trace } from 'mobx'; import { Doc, DocListCastAsync } from '../../new_fields/Doc'; import { Id } from '../../new_fields/FieldSymbols'; import { Cast, NumCast, StrCast } from '../../new_fields/Types'; @@ -196,10 +196,13 @@ export class DocumentManager { const second = secondDocWithoutView ? [secondDocWithoutView] : secondDocs; const linkDoc = first.length ? first[0] : second.length ? second[0] : undefined; const linkFollowDocs = first.length ? [await first[0].anchor2 as Doc, await first[0].anchor1 as Doc] : second.length ? [await second[0].anchor1 as Doc, await second[0].anchor2 as Doc] : undefined; - const linkFollowDocContexts = first.length ? [await first[0].targetContext as Doc, await first[0].sourceContext as Doc] : second.length ? [await second[0].sourceContext as Doc, await second[0].targetContext as Doc] : [undefined, undefined]; + const linkFollowDocContexts = first.length ? [await first[0].anchor2Context as Doc, await first[0].anchor1Context as Doc] : second.length ? [await second[0].anchor1Context as Doc, await second[0].anchor2Context as Doc] : [undefined, undefined]; + const linkFollowTimecodes = first.length ? [NumCast(first[0].anchor2Timecode), NumCast(first[0].anchor1Timecode)] : second.length ? [NumCast(second[0].anchor1Timecode), NumCast(second[0].anchor2Timecode)] : [undefined, undefined]; if (linkFollowDocs && linkDoc) { const maxLocation = StrCast(linkFollowDocs[0].maximizeLocation, "inTab"); const targetContext = !Doc.AreProtosEqual(linkFollowDocContexts[reverse ? 1 : 0], currentContext) ? linkFollowDocContexts[reverse ? 1 : 0] : undefined; + const target = linkFollowDocs[reverse ? 1 : 0]; + target.currentTimecode !== undefined && (target.currentTimecode = linkFollowTimecodes[reverse ? 1 : 0]); DocumentManager.Instance.jumpToDocument(linkFollowDocs[reverse ? 1 : 0], zoom, (doc: Doc) => focus(doc, maxLocation), targetContext, linkDoc[Id]); } } diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index 1cfa8d644..9ab320eab 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -87,7 +87,7 @@ export class InkingCanvas extends React.Component { color: InkingControl.Instance.selectedColor, width: InkingControl.Instance.selectedWidth, tool: InkingControl.Instance.selectedTool, - page: NumCast(this.props.Document.curPage, -1) + displayTimecode: NumCast(this.props.Document.currentTimecode, -1) }); this.inkData = data; } @@ -150,9 +150,9 @@ export class InkingCanvas extends React.Component { @computed get drawnPaths() { - let curPage = NumCast(this.props.Document.curPage, -1); + let curTimecode = NumCast(this.props.Document.currentTimecode, -1); let paths = Array.from(this.inkData).reduce((paths, [id, strokeData]) => { - if (strokeData.page === -1 || (Math.abs(Math.round(strokeData.page) - Math.round(curPage)) < 3)) { + if (strokeData.displayTimecode === -1 || (Math.abs(Math.round(strokeData.displayTimecode) - Math.round(curTimecode)) < 3)) { paths.push( { @action.bound addDocument(doc: Doc, allowDuplicates: boolean = false): boolean { - var curPage = NumCast(this.props.Document.curPage, -1); - Doc.GetProto(doc).page = curPage; + var curTime = NumCast(this.props.Document.currentTimecode, -1); + curTime !== -1 && (doc.displayTimecode = curTime); if (this.props.fieldExt) { // bcz: fieldExt !== undefined means this is an overlay layer Doc.GetProto(doc).annotationOn = this.props.Document; } diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 52a41fc84..1ba35a52b 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -51,7 +51,7 @@ const columnTypes: Map = new Map([ ["title", ColumnType.String], ["x", ColumnType.Number], ["y", ColumnType.Number], ["width", ColumnType.Number], ["height", ColumnType.Number], ["nativeWidth", ColumnType.Number], ["nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean], - ["page", ColumnType.Number], ["curPage", ColumnType.Number], ["zIndex", ColumnType.Number] + ["page", ColumnType.Number], ["curPage", ColumnType.Number], ["currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number] ]); @observer diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 5185d9d0e..3d898b7de 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -21,7 +21,7 @@ export class CollectionVideoView extends React.Component { } private get uIButtons() { let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale); - let curTime = NumCast(this.props.Document.curPage); + let curTime = NumCast(this.props.Document.currentTimecode); return ([
{"" + Math.round(curTime)} {" " + Math.round((curTime - Math.trunc(curTime)) * 100)} @@ -85,7 +85,7 @@ export class CollectionVideoView extends React.Component { onPointerMove = (e: PointerEvent) => { this._isclick += Math.abs(e.movementX) + Math.abs(e.movementY); if (this._videoBox) { - this._videoBox.Seek(Math.max(0, NumCast(this.props.Document.curPage, 0) + Math.sign(e.movementX) * 0.0333)); + this._videoBox.Seek(Math.max(0, NumCast(this.props.Document.currentTimecode, 0) + Math.sign(e.movementX) * 0.0333)); } e.stopImmediatePropagation(); } @@ -94,7 +94,7 @@ export class CollectionVideoView extends React.Component { document.removeEventListener("pointermove", this.onPointerMove, true); document.removeEventListener("pointerup", this.onPointerUp, true); InkingControl.Instance.switchTool(InkTool.None); - this._isclick < 10 && (this.props.Document.curPage = 0); + this._isclick < 10 && (this.props.Document.currentTimecode = 0); } setVideoBox = (videoBox: VideoBox) => { this._videoBox = videoBox; }; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 7b29f2c2a..bfd3e6481 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -105,7 +105,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { SelectionManager.DeselectAll(); docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).map(dv => dv && SelectionManager.SelectDoc(dv, true)); } - public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.page, -1) - NumCast(this.Document.curPage, -1)) < 1.5 || NumCast(doc.page, -1) === -1); } + public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); } public getActiveDocuments = () => { return this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index bf154d37d..eaf65b88c 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -285,7 +285,7 @@ export class MarqueeView extends React.Component this.props.removeDocument(d); d.x = NumCast(d.x) - bounds.left - bounds.width / 2; d.y = NumCast(d.y) - bounds.top - bounds.height / 2; - d.page = -1; + d.displayTimecode = undefined; return d; }); } diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index 2bff3ded4..b18aa5d63 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -128,7 +128,7 @@ export class LinkFollowBox extends React.Component { runInAction(async () => { this._docs = docs.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).map(doc => ({ col: doc, target: dest })); this._otherDocs = Array.from(map.entries()).filter(entry => !Doc.AreProtosEqual(entry[0], CollectionDockingView.Instance.props.Document)).map(([col, target]) => ({ col, target })); - let tcontext = LinkFollowBox.linkDoc && (await Cast(LinkFollowBox.linkDoc.targetContext, Doc)) as Doc; + let tcontext = LinkFollowBox.linkDoc && (await Cast(LinkFollowBox.linkDoc.anchor2Context, Doc)) as Doc; runInAction(() => tcontext && this._docs.splice(0, 0, { col: tcontext, target: dest })); }); } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 88cd2cdc4..1f3887608 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -65,9 +65,9 @@ export class PDFBox extends DocComponent(PdfDocumen public search(string: string, fwd: boolean) { this._pdfViewer && this._pdfViewer.search(string, fwd); } public prevAnnotation() { this._pdfViewer && this._pdfViewer.prevAnnotation(); } public nextAnnotation() { this._pdfViewer && this._pdfViewer.nextAnnotation(); } - public backPage() { this._pdfViewer!.gotoPage(NumCast(this.props.Document.curPage) - 1); } + public backPage() { this._pdfViewer!.gotoPage((this.Document.curPage || 1) - 1); } public gotoPage = (p: number) => { this._pdfViewer!.gotoPage(p); }; - public forwardPage() { this._pdfViewer!.gotoPage(NumCast(this.props.Document.curPage) + 1); } + public forwardPage() { this._pdfViewer!.gotoPage((this.Document.curPage || 1) + 1); } @undoBatch @action @@ -128,7 +128,7 @@ export class PDFBox extends DocComponent(PdfDocumen
e.stopPropagation()}>
- this.gotoPage(Number(e.currentTarget.value))} style={{ left: 20, top: 5, height: "30px", width: "30px", position: "absolute", pointerEvents: "all" }} onClick={action(() => this._pageControls = !this._pageControls)} /> diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 573197117..e83aa8bea 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -3,7 +3,7 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction, import { observer } from "mobx-react"; import * as rp from 'request-promise'; import { InkTool } from "../../../new_fields/InkField"; -import { makeInterface } from "../../../new_fields/Schema"; +import { makeInterface, createSchema } from "../../../new_fields/Schema"; import { Cast, FieldValue, NumCast, BoolCast } from "../../../new_fields/Types"; import { VideoField } from "../../../new_fields/URLField"; import { RouteStore } from "../../../server/RouteStore"; @@ -16,16 +16,19 @@ import { DocumentDecorations } from "../DocumentDecorations"; import { InkingControl } from "../InkingControl"; import { documentSchema } from "./DocumentView"; import { FieldView, FieldViewProps } from './FieldView'; -import { pageSchema } from "./ImageBox"; import "./VideoBox.scss"; import { library } from "@fortawesome/fontawesome-svg-core"; import { faVideo } from "@fortawesome/free-solid-svg-icons"; import { Doc } from "../../../new_fields/Doc"; import { ScriptField } from "../../../new_fields/ScriptField"; +import { positionSchema } from "./CollectionFreeFormDocumentView"; var path = require('path'); -type VideoDocument = makeInterface<[typeof documentSchema, typeof pageSchema]>; -const VideoDocument = makeInterface(documentSchema, pageSchema); +export const timeSchema = createSchema({ + currentTimecode: "number", +}); +type VideoDocument = makeInterface<[typeof documentSchema, typeof positionSchema, typeof timeSchema]>; +const VideoDocument = makeInterface(documentSchema, positionSchema, timeSchema); library.add(faVideo); @@ -97,11 +100,11 @@ export class VideoBox extends DocComponent(VideoD } @action public Snapshot() { - let width = NumCast(this.props.Document.width); - let height = NumCast(this.props.Document.height); + let width = this.Document.width || 0; + let height = this.Document.height || 0; var canvas = document.createElement('canvas'); canvas.width = 640; - canvas.height = 640 * NumCast(this.props.Document.nativeHeight) / NumCast(this.props.Document.nativeWidth); + canvas.height = 640 * (this.Document.nativeHeight || 0) / (this.Document.nativeWidth || 1); var ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions if (ctx) { ctx.rect(0, 0, canvas.width, canvas.height); @@ -112,24 +115,25 @@ export class VideoBox extends DocComponent(VideoD if (!this._videoRef) { // can't find a way to take snapshots of videos let b = Docs.Create.ButtonDocument({ - x: NumCast(this.props.Document.x) + width, y: NumCast(this.props.Document.y), - width: 150, height: 50, title: NumCast(this.props.Document.curPage).toString() + x: (this.Document.x || 0) + width, y: (this.Document.y || 0), + width: 150, height: 50, title: (this.Document.currentTimecode || 0).toString() }); - b.onClick = ScriptField.MakeScript(`this.curPage = ${NumCast(this.props.Document.curPage)}`); + b.onClick = ScriptField.MakeScript(`this.currentTimecode = ${(this.Document.currentTimecode || 0)}`); } else { //convert to desired file format var dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png' // if you want to preview the captured image, - let filename = encodeURIComponent("snapshot" + this.props.Document.title + "_" + this.props.Document.curPage).replace(/\./g, ""); - VideoBox.convertDataUri(dataUrl, filename).then(returnedFilename => { + let filename = path.basename(encodeURIComponent("snapshot" + this.Document.title + "_" + (this.Document.currentTimecode || 0).toString())); + VideoBox.convertDataUri(dataUrl, filename.replace(/\..*$/, "")).then(returnedFilename => { if (returnedFilename) { let url = this.choosePath(Utils.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-" + x: (this.Document.x || 0) + width, y: (this.Document.y || 0), + width: 150, height: height / width * 150, title: "--snapshot" + (this.Document.currentTimecode || 0) + " image-" }); + imageSummary.isButton = true; this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.addDocument && this.props.ContainingCollectionView.props.addDocument(imageSummary, false); - DocUtils.MakeLink({doc:imageSummary}, {doc: this.props.Document}, "snapshot from " + this.props.Document.title, "video frame snapshot"); + DocUtils.MakeLink({ doc: imageSummary }, { doc: this.props.Document }, "snapshot from " + this.Document.title, "video frame snapshot"); } }); } @@ -137,8 +141,8 @@ export class VideoBox extends DocComponent(VideoD @action updateTimecode = () => { - this.player && (this.props.Document.curPage = this.player.currentTime); - this._youtubePlayer && (this.props.Document.curPage = this._youtubePlayer.getCurrentTime()); + this.player && (this.Document.currentTimecode = this.player.currentTime); + this._youtubePlayer && (this.Document.currentTimecode = this._youtubePlayer.getCurrentTime()); } componentDidMount() { @@ -146,12 +150,12 @@ export class VideoBox extends DocComponent(VideoD if (this.youtubeVideoId) { let youtubeaspect = 400 / 315; - var nativeWidth = FieldValue(this.Document.nativeWidth, 0); - var nativeHeight = FieldValue(this.Document.nativeHeight, 0); + var nativeWidth = (this.Document.nativeWidth || 0); + var nativeHeight = (this.Document.nativeHeight || 0); if (!nativeWidth || !nativeHeight) { if (!this.Document.nativeWidth) this.Document.nativeWidth = 600; this.Document.nativeHeight = this.Document.nativeWidth / youtubeaspect; - this.Document.height = FieldValue(this.Document.width, 0) / youtubeaspect; + this.Document.height = (this.Document.width || 0) / youtubeaspect; } } } @@ -168,10 +172,9 @@ export class VideoBox extends DocComponent(VideoD if (vref) { this._videoRef!.ontimeupdate = this.updateTimecode; vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen); - if (this._reactionDisposer) this._reactionDisposer(); - this._reactionDisposer = reaction(() => this.props.Document.curPage, () => - !this.Playing && (vref.currentTime = this.Document.curPage || 0) - , { fireImmediately: true }); + this._reactionDisposer && this._reactionDisposer(); + this._reactionDisposer = reaction(() => this.Document.currentTimecode || 0, + time => !this.Playing && (vref.currentTime = time), { fireImmediately: true }); } } @@ -242,7 +245,7 @@ export class VideoBox extends DocComponent(VideoD let onYoutubePlayerReady = (event: any) => { this._reactionDisposer && this._reactionDisposer(); this._youtubeReactionDisposer && this._youtubeReactionDisposer(); - this._reactionDisposer = reaction(() => this.props.Document.curPage, () => !this.Playing && this.Seek(this.Document.curPage || 0)); + this._reactionDisposer = reaction(() => this.Document.currentTimecode, () => !this.Playing && this.Seek(this.Document.currentTimecode || 0)); this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), DocumentDecorations.Instance.Interacting, InkingControl.Instance.selectedTool], () => { let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected() && !DocumentDecorations.Instance.Interacting; iframe.style.pointerEvents = interactive ? "all" : "none"; @@ -263,9 +266,9 @@ export class VideoBox extends DocComponent(VideoD this._youtubeIframeId = VideoBox._youtubeIframeCounter++; this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true; let style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : ""); - let start = untracked(() => Math.round(NumCast(this.props.Document.curPage))); + let start = untracked(() => Math.round(this.Document.currentTimecode || 0)); return ; } diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts index 8f64c1c2e..e381d0218 100644 --- a/src/new_fields/InkField.ts +++ b/src/new_fields/InkField.ts @@ -16,7 +16,7 @@ export interface StrokeData { color: string; width: string; tool: InkTool; - page: number; + displayTimecode: number; } export type InkData = Map; -- cgit v1.2.3-70-g09d2 From d970ad82bc7b0b1c8dae27b5f55ad78bddb0f7cd Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 7 Oct 2019 21:48:01 -0400 Subject: added commeting with (( and ## --- src/client/documents/Documents.ts | 1 + src/client/util/RichTextRules.ts | 46 ++++++++++++++++++++++++++--- src/client/util/RichTextSchema.tsx | 3 +- src/client/views/nodes/FormattedTextBox.tsx | 2 +- 4 files changed, 46 insertions(+), 6 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 98f56af01..34e146f0e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -73,6 +73,7 @@ export interface DocumentOptions { dropAction?: dropActionType; backgroundLayout?: string; chromeStatus?: string; + fontSize?: number; curPage?: number; currentTimecode?: number; documentText?: string; diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts index 2d5ec1743..2ff758fbc 100644 --- a/src/client/util/RichTextRules.ts +++ b/src/client/util/RichTextRules.ts @@ -1,10 +1,12 @@ import { textblockTypeInputRule, smartQuotes, emDash, ellipsis, InputRule } from "prosemirror-inputrules"; import { schema } from "./RichTextSchema"; import { wrappingInputRule } from "./prosemirrorPatches"; -import { NodeSelection } from "prosemirror-state"; +import { NodeSelection, TextSelection } from "prosemirror-state"; import { NumCast, Cast } from "../../new_fields/Types"; import { Doc } from "../../new_fields/Doc"; import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; +import { Docs } from "../documents/Documents"; +import { Id } from "../../new_fields/FieldSymbols"; export const inpRules = { rules: [ @@ -80,8 +82,9 @@ export const inpRules = { ruleProvider["ruleAlign_" + heading] = "center"; return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr; } - return node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "center" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : + let replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "center" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr; + return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2))); }), new InputRule( new RegExp(/^\[\[\s$/), @@ -94,8 +97,9 @@ export const inpRules = { ruleProvider["ruleAlign_" + heading] = "left"; return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr; } - return node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "left" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : + let replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "left" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr; + return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2))); }), new InputRule( new RegExp(/^\]\]\s$/), @@ -108,8 +112,42 @@ export const inpRules = { ruleProvider["ruleAlign_" + heading] = "right"; return node ? state.tr.deleteRange(start, end).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr; } - return node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "right" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : + let replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "right" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr; + return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2))); + }), + new InputRule( + new RegExp(/##\s$/), + (state, match, start, end) => { + let node = (state.doc.resolve(start) as any).nodeAfter; + let sm = state.storedMarks || undefined; + let target = Docs.Create.TextDocument({ width: 75, height: 35, autoHeight: true, fontSize: 9, title: "inline comment" }); + let replaced = node ? state.tr.insertText("←", start).replaceRangeWith(start + 1, end + 1, schema.nodes.dashDoc.create({ + width: 75, height: 35, + title: "dashDoc", docid: target[Id], + float: "right" + })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : + state.tr; + return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 1))); + }), + new InputRule( + new RegExp(/\(\(/), + (state, match, start, end) => { + let node = (state.doc.resolve(start) as any).nodeAfter; + let sm = state.storedMarks || undefined; + let mark = state.schema.marks.highlight.create(); + let selected = state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).addMark(start, end, mark); + let content = selected.selection.content(); + let replaced = node ? selected.replaceRangeWith(start, start, + schema.nodes.star.create({ visibility: true, text: content, textslice: content.toJSON() })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : + state.tr; + return replaced.setSelection(new TextSelection(replaced.doc.resolve(end + 1))); + }), + new InputRule( + new RegExp(/\)\)/), + (state, match, start, end) => { + let mark = state.schema.marks.highlight.create(); + return state.tr.removeStoredMark(mark); }), new InputRule( new RegExp(/\^f\s$/), diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 8d0f59ddc..047706368 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -739,7 +739,8 @@ export class DashDocView { this._dashSpan.style.position = "absolute"; this._dashSpan.style.display = "inline-block"; let removeDoc = () => { - let ns = new NodeSelection(view.state.doc.resolve(getPos())); + let pos = getPos(); + let ns = new NodeSelection(view.state.doc.resolve(pos)); view.dispatch(view.state.tr.setSelection(ns).deleteSelection()); return true; }; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index cab408a31..cacef1ac3 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -457,7 +457,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe }, action((rules: any) => { this._fontFamily = rules ? rules.font : "Arial"; - this._fontSize = rules ? rules.size : 13; + this._fontSize = rules ? rules.size : NumCast(this.props.Document.fontSize, 13); rules && setTimeout(() => { const view = this._editorView!; if (this._proseRef) { -- cgit v1.2.3-70-g09d2 From 77d66d159d75442ff5635c4bf4843b6155883cc2 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 10 Oct 2019 14:04:22 -0400 Subject: removed CollectionVideoView --- src/client/documents/Documents.ts | 3 +- src/client/util/DocumentManager.ts | 5 +- src/client/util/SharingManager.tsx | 3 +- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/PreviewCursor.tsx | 18 +-- .../views/collections/CollectionBaseView.tsx | 15 +- .../views/collections/CollectionSchemaCells.tsx | 7 +- .../views/collections/CollectionSchemaView.tsx | 9 +- src/client/views/collections/CollectionSubView.tsx | 17 +- .../views/collections/CollectionVideoView.scss | 51 ------ .../views/collections/CollectionVideoView.tsx | 115 ------------- .../CollectionFreeFormLinkView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 8 +- .../collections/collectionFreeForm/MarqueeView.tsx | 14 +- src/client/views/linking/LinkFollowBox.tsx | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 7 +- src/client/views/nodes/FieldView.tsx | 5 +- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/nodes/VideoBox.scss | 57 ++++++- src/client/views/nodes/VideoBox.tsx | 177 +++++++++++++++++++-- src/client/views/nodes/WebBox.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 18 ++- 23 files changed, 284 insertions(+), 258 deletions(-) delete mode 100644 src/client/views/collections/CollectionVideoView.scss delete mode 100644 src/client/views/collections/CollectionVideoView.tsx (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9d1a6ed3e..22aa74634 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1,7 +1,6 @@ import { HistogramField } from "../northstar/dash-fields/HistogramField"; import { HistogramBox } from "../northstar/dash-nodes/HistogramBox"; import { HistogramOperation } from "../northstar/operations/HistogramOperation"; -import { CollectionVideoView } from "../views/collections/CollectionVideoView"; import { CollectionView } from "../views/collections/CollectionView"; import { CollectionViewType } from "../views/collections/CollectionBaseView"; import { AudioBox } from "../views/nodes/AudioBox"; @@ -137,7 +136,7 @@ export namespace Docs { options: { height: 150 } }], [DocumentType.VID, { - layout: { view: VideoBox, collectionView: [CollectionVideoView, data, anno] as CollectionViewType }, + layout: { view: VideoBox, ext: anno }, options: { currentTimecode: 0 }, }], [DocumentType.AUDIO, { diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index c95d923cb..24285a70a 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -3,7 +3,6 @@ import { Doc, DocListCastAsync } from '../../new_fields/Doc'; import { Id } from '../../new_fields/FieldSymbols'; import { Cast, NumCast, StrCast } from '../../new_fields/Types'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; -import { CollectionVideoView } from '../views/collections/CollectionVideoView'; import { CollectionView } from '../views/collections/CollectionView'; import { DocumentView } from '../views/nodes/DocumentView'; import { LinkManager } from './LinkManager'; @@ -56,7 +55,7 @@ export class DocumentManager { return this.getDocumentViewsById(doc[Id]); } - public getDocumentViewById(id: string, preferredCollection?: CollectionView | CollectionVideoView): DocumentView | undefined { + public getDocumentViewById(id: string, preferredCollection?: CollectionView): DocumentView | undefined { let toReturn: DocumentView | undefined; let passes = preferredCollection ? [preferredCollection, undefined] : [undefined]; @@ -81,7 +80,7 @@ export class DocumentManager { return toReturn; } - public getDocumentView(toFind: Doc, preferredCollection?: CollectionView | CollectionVideoView): DocumentView | undefined { + public getDocumentView(toFind: Doc, preferredCollection?: CollectionView): DocumentView | undefined { return this.getDocumentViewById(toFind[Id], preferredCollection); } diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index c989b6c17..d37cd1b80 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -17,7 +17,6 @@ import * as fa from '@fortawesome/free-solid-svg-icons'; import { DocumentView } from "../views/nodes/DocumentView"; import { SelectionManager } from "./SelectionManager"; import { DocumentManager } from "./DocumentManager"; -import { CollectionVideoView } from "../views/collections/CollectionVideoView"; import { CollectionView } from "../views/collections/CollectionView"; library.add(fa.faCopy); @@ -185,7 +184,7 @@ export default class SharingManager extends React.Component<{}> { className={"focus-span"} title={title} onClick={() => { - let context: Opt; + let context: Opt; if (this.targetDoc && this.targetDocView && (context = this.targetDocView.props.ContainingCollectionView)) { DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, context.props.Document); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 4f9bdbe9c..1d9f0c74b 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -333,7 +333,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> iconDoc.y = NumCast(doc.y) - 24; iconDoc.maximizedDocs = new List(selected.map(s => s.props.Document)); selected.length === 1 && (doc.minimizedDoc = iconDoc); - selected[0].props.addDocument && selected[0].props.addDocument(iconDoc, false); + selected[0].props.addDocument && selected[0].props.addDocument(iconDoc); return iconDoc; } @action diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 1aed51e64..eed2cc5da 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -14,7 +14,7 @@ export class PreviewCursor extends React.Component<{}> { static _onKeyPress?: (e: KeyboardEvent) => void; static _getTransform: () => Transform; static _addLiveTextDoc: (doc: Doc) => void; - static _addDocument: (doc: Doc, allowDuplicates: false) => boolean; + static _addDocument: (doc: Doc) => boolean; @observable static _clickPoint = [0, 0]; @observable public static Visible = false; //when focus is lost, this will remove the preview cursor @@ -44,7 +44,7 @@ export class PreviewCursor extends React.Component<{}> { title: url, width: 400, height: 315, nativeWidth: 600, nativeHeight: 472.5, x: newPoint[0], y: newPoint[1] - }), false); + })); return; } @@ -56,7 +56,7 @@ export class PreviewCursor extends React.Component<{}> { title: url, width: 300, height: 300, // nativeWidth: 300, nativeHeight: 472.5, x: newPoint[0], y: newPoint[1] - }), false); + })); return; } @@ -79,11 +79,11 @@ export class PreviewCursor extends React.Component<{}> { let img: Doc = Docs.Create.ImageDocument( arr[1], { - width: 300, title: arr[1], - x: newPoint[0], - y: newPoint[1], - }); - PreviewCursor._addDocument(img, false); + width: 300, title: arr[1], + x: newPoint[0], + y: newPoint[1], + }); + PreviewCursor._addDocument(img); return; } @@ -113,7 +113,7 @@ export class PreviewCursor extends React.Component<{}> { onKeyPress: (e: KeyboardEvent) => void, addLiveText: (doc: Doc) => void, getTransform: () => Transform, - addDocument: (doc: Doc, allowDuplicates: false) => boolean) { + addDocument: (doc: Doc) => boolean) { this._clickPoint = [x, y]; this._onKeyPress = onKeyPress; this._addLiveTextDoc = addLiveText; diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 62be1fc31..61919427a 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -45,7 +45,7 @@ export namespace CollectionViewType { } export interface CollectionRenderProps { - addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + addDocument: (document: Doc) => boolean; removeDocument: (document: Doc) => boolean; moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; active: () => boolean; @@ -100,22 +100,13 @@ export class CollectionBaseView extends React.Component { @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); } @action.bound - addDocument(doc: Doc, allowDuplicates: boolean = false): boolean { - var curTime = NumCast(this.props.Document.currentTimecode, -1); - curTime !== -1 && (doc.displayTimecode = curTime); + addDocument(doc: Doc): boolean { if (this.props.fieldExt) { // bcz: fieldExt !== undefined means this is an overlay layer Doc.GetProto(doc).annotationOn = this.props.Document; } 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(targetDataDoc)[targetField] = new List([doc]); - } + Doc.AddDocToList(targetDataDoc, targetField, doc); Doc.GetProto(doc).lastOpened = new DateField; return true; } diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index fd1362848..79c032723 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -14,15 +14,12 @@ import '../DocumentDecorations.scss'; import { EditableView } from "../EditableView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./CollectionSchemaView.scss"; -import { CollectionVideoView } from "./CollectionVideoView"; import { CollectionView } from "./CollectionView"; import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../new_fields/Types"; import { Docs } from "../../documents/Documents"; -import { DocumentContentsView } from "../nodes/DocumentContentsView"; import { SelectionManager } from "../../util/SelectionManager"; import { library } from '@fortawesome/fontawesome-svg-core'; import { faExpand } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import { KeyCodes } from "../../northstar/utils/KeyCodes"; import { undoBatch } from "../../util/UndoManager"; @@ -33,8 +30,8 @@ export interface CellProps { row: number; col: number; rowProps: CellInfo; - CollectionView: Opt; - ContainingCollection: Opt; + CollectionView: Opt; + ContainingCollection: Opt; Document: Doc; fieldKey: string; renderDepth: number; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 670c6bd43..3218f630a 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -23,7 +23,6 @@ import '../DocumentDecorations.scss'; import { DocumentView } from "../nodes/DocumentView"; import "./CollectionSchemaView.scss"; import { CollectionSubView } from "./CollectionSubView"; -import { CollectionVideoView } from "./CollectionVideoView"; import { CollectionView } from "./CollectionView"; import { undoBatch } from "../../util/UndoManager"; import { CollectionSchemaHeader, CollectionSchemaAddColumnHeader } from "./CollectionSchemaHeaders"; @@ -246,8 +245,8 @@ export interface SchemaTableProps { PanelHeight: () => number; PanelWidth: () => number; childDocs?: Doc[]; - CollectionView: Opt; - ContainingCollectionView: Opt; + CollectionView: Opt; + ContainingCollectionView: Opt; ContainingCollectionDoc: Opt; fieldKey: string; renderDepth: number; @@ -904,11 +903,11 @@ interface CollectionSchemaPreviewProps { ruleProvider: Doc | undefined; focus?: (doc: Doc) => void; showOverlays?: (doc: Doc) => { title?: string, caption?: string }; - CollectionView?: CollectionView | CollectionVideoView; + CollectionView?: CollectionView; CollectionDoc?: Doc; onClick?: ScriptField; getTransform: () => Transform; - addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + addDocument: (document: Doc) => boolean; moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean; removeDocument: (document: Doc) => boolean; active: () => boolean; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 5e2b79278..689adc375 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -18,7 +18,6 @@ import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocComponent } from "../DocComponent"; import { FieldViewProps } from "../nodes/FieldView"; import { FormattedTextBox, GoogleRef } from "../nodes/FormattedTextBox"; -import { CollectionVideoView } from "./CollectionVideoView"; import { CollectionView } from "./CollectionView"; import React = require("react"); var path = require('path'); @@ -26,7 +25,7 @@ import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; export interface CollectionViewProps extends FieldViewProps { - addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + addDocument: (document: Doc) => boolean; removeDocument: (document: Doc) => boolean; moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; PanelWidth: () => number; @@ -37,7 +36,7 @@ export interface CollectionViewProps extends FieldViewProps { } export interface SubCollectionViewProps extends CollectionViewProps { - CollectionView: Opt; + CollectionView: Opt; ruleProvider: Doc | undefined; } @@ -175,14 +174,14 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { DocServer.GetRefField(docid).then(f => { if (f instanceof Doc) { if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView - (f instanceof Doc) && this.props.addDocument(f, false); + (f instanceof Doc) && this.props.addDocument(f); } }); } else { this.props.addDocument && this.props.addDocument(Docs.Create.WebDocument(href, options)); } } else if (text) { - this.props.addDocument && this.props.addDocument(Docs.Create.TextDocument({ ...options, width: 100, height: 25, documentText: "@@@" + text }), false); + this.props.addDocument && this.props.addDocument(Docs.Create.TextDocument({ ...options, width: 100, height: 25, documentText: "@@@" + text })); } return; } @@ -194,7 +193,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { let split = img.split("src=\"")[1].split("\"")[0]; let doc = Docs.Create.ImageDocument(split, { ...options, width: 300 }); ImageUtils.ExtractExif(doc); - this.props.addDocument(doc, false); + this.props.addDocument(doc); return; } else { let path = window.location.origin + "/doc/"; @@ -203,12 +202,12 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { DocServer.GetRefField(docid).then(f => { if (f instanceof Doc) { if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView - (f instanceof Doc) && this.props.addDocument(f, false); + (f instanceof Doc) && this.props.addDocument(f); } }); } else { let htmlDoc = Docs.Create.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text }); - this.props.addDocument(htmlDoc, false); + this.props.addDocument(htmlDoc); } return; } @@ -252,7 +251,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { let type = result["content-type"]; if (type) { Docs.Get.DocumentFromType(type, str, { ...options, width: 300, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300 }) - .then(doc => doc && this.props.addDocument(doc, false)); + .then(doc => doc && this.props.addDocument(doc)); } }); promises.push(prom); diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss deleted file mode 100644 index 509851ebb..000000000 --- a/src/client/views/collections/CollectionVideoView.scss +++ /dev/null @@ -1,51 +0,0 @@ - -.collectionVideoView-cont{ - width: 100%; - height: 100%; - position: inherit; - top: 0; - left:0; - z-index: -1; - display:inline-table; -} -.collectionVideoView-time{ - color : white; - top :25px; - left : 25px; - position: absolute; - background-color: rgba(50, 50, 50, 0.2); - transform-origin: left top; -} -.collectionVideoView-snapshot{ - color : white; - top :25px; - right : 25px; - position: absolute; - background-color: rgba(50, 50, 50, 0.2); - transform-origin: left top; -} -.collectionVideoView-play { - width: 25px; - height: 20px; - bottom: 25px; - left : 25px; - position: absolute; - color : white; - background-color: rgba(50, 50, 50, 0.2); - border-radius: 4px; - text-align: center; - transform-origin: left bottom; -} -.collectionVideoView-full { - width: 25px; - height: 20px; - bottom: 25px; - right : 25px; - position: absolute; - color : white; - background-color: rgba(50, 50, 50, 0.2); - border-radius: 4px; - text-align: center; - transform-origin: right bottom; - -} \ No newline at end of file diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx deleted file mode 100644 index 3d898b7de..000000000 --- a/src/client/views/collections/CollectionVideoView.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { action } from "mobx"; -import { observer } from "mobx-react"; -import { NumCast } from "../../../new_fields/Types"; -import { FieldView, FieldViewProps } from "../nodes/FieldView"; -import { VideoBox } from "../nodes/VideoBox"; -import { CollectionBaseView, CollectionRenderProps, CollectionViewType } from "./CollectionBaseView"; -import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; -import "./CollectionVideoView.scss"; -import React = require("react"); -import { InkingControl } from "../InkingControl"; -import { InkTool } from "../../../new_fields/InkField"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - - -@observer -export class CollectionVideoView extends React.Component { - private _videoBox?: VideoBox; - - 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); - let curTime = NumCast(this.props.Document.currentTimecode); - return ([
- {"" + Math.round(curTime)} - {" " + Math.round((curTime - Math.trunc(curTime)) * 100)} -
, -
- -
, - VideoBox._showControls ? (null) : [ -
- -
, -
- F -
- ]]); - } - - @action - onPlayDown = () => { - if (this._videoBox) { - if (this._videoBox.Playing) { - this._videoBox.Pause(); - } else { - this._videoBox.Play(); - } - } - } - - @action - onFullDown = (e: React.PointerEvent) => { - if (this._videoBox) { - this._videoBox.FullScreen(); - e.stopPropagation(); - e.preventDefault(); - } - } - - @action - onSnapshot = (e: React.PointerEvent) => { - if (this._videoBox) { - this._videoBox.Snapshot(); - e.stopPropagation(); - e.preventDefault(); - } - } - - _isclick = 0; - @action - onResetDown = (e: React.PointerEvent) => { - if (this._videoBox) { - this._videoBox.Pause(); - e.stopPropagation(); - this._isclick = 0; - document.addEventListener("pointermove", this.onPointerMove, true); - document.addEventListener("pointerup", this.onPointerUp, true); - InkingControl.Instance.switchTool(InkTool.Eraser); - } - } - - @action - onPointerMove = (e: PointerEvent) => { - this._isclick += Math.abs(e.movementX) + Math.abs(e.movementY); - if (this._videoBox) { - this._videoBox.Seek(Math.max(0, NumCast(this.props.Document.currentTimecode, 0) + Math.sign(e.movementX) * 0.0333)); - } - e.stopImmediatePropagation(); - } - @action - onPointerUp = (e: PointerEvent) => { - document.removeEventListener("pointermove", this.onPointerMove, true); - document.removeEventListener("pointerup", this.onPointerUp, true); - InkingControl.Instance.switchTool(InkTool.None); - this._isclick < 10 && (this.props.Document.currentTimecode = 0); - } - setVideoBox = (videoBox: VideoBox) => { this._videoBox = videoBox; }; - - private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => { - let props = { ...this.props, ...renderProps }; - return (<> - - {this.props.isSelected() ? this.uIButtons : (null)} - ); - } - - render() { - return ( - - {this.subView} - ); - } -} \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index df089eb00..fe92eed10 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -10,7 +10,7 @@ export interface CollectionFreeFormLinkViewProps { A: Doc; B: Doc; LinkDocs: Doc[]; - addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + addDocument: (document: Doc) => boolean; removeDocument: (document: Doc) => boolean; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 38488f033..d2644480c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -93,10 +93,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { heading = !sorted.length ? Math.max(1, maxHeading) : NumCast(sorted[sorted.length - 1].heading) === 1 ? 2 : NumCast(sorted[sorted.length - 1].heading); } !this.Document.isRuleProvider && (newBox.heading = heading); - this.addDocument(newBox, false); + this.addDocument(newBox); } - private addDocument = (newBox: Doc, allowDuplicates: boolean) => { - let added = this.props.addDocument(newBox, false); + private addDocument = (newBox: Doc) => { + let added = this.props.addDocument(newBox); added && this.bringToFront(newBox); added && this.updateCluster(newBox); return added; @@ -659,7 +659,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { if (doc instanceof Doc) { const [xx, yy] = this.props.ScreenToLocalTransform().transformPoint(x, y); doc.x = xx, doc.y = yy; - this.props.addDocument && this.props.addDocument(doc, false); + this.props.addDocument && this.props.addDocument(doc); } } } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index eaf65b88c..bb787106c 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -24,7 +24,7 @@ interface MarqueeViewProps { getContainerTransform: () => Transform; getTransform: () => Transform; container: CollectionFreeFormView; - addDocument: (doc: Doc, allowDuplicates: false) => boolean; + addDocument: (doc: Doc) => boolean; activeDocuments: () => Doc[]; selectDocuments: (docs: Doc[]) => void; removeDocument: (doc: Doc) => boolean; @@ -83,7 +83,7 @@ export class MarqueeView extends React.Component ns.map(line => { let indent = line.search(/\S|$/); 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); + this.props.addDocument(newBox); y += 40 * this.props.getTransform().Scale; }); })(); @@ -92,7 +92,7 @@ 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.Create.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 }));// paste an image from its URL in the paste buffer } else { this.pasteTable(ns, x, y); } @@ -146,7 +146,7 @@ export class MarqueeView extends React.Component } let newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 }); - this.props.addDocument(newCol, false); + this.props.addDocument(newCol); } } @action @@ -202,7 +202,7 @@ export class MarqueeView extends React.Component } } - setPreviewCursor = (x: number, y: number, drag: boolean) => { + setPreviewCursor = action((x: number, y: number, drag: boolean) => { if (drag) { this._downX = this._lastX = x; this._downY = this._lastY = y; @@ -217,7 +217,7 @@ export class MarqueeView extends React.Component this._downY = y; PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument); } - } + }) @action onClick = (e: React.MouseEvent): void => { @@ -350,7 +350,7 @@ export class MarqueeView extends React.Component } } else { - this.props.addDocument(newCollection, false); + this.props.addDocument(newCollection); this.props.selectDocuments([newCollection]); } this.cleanupInteractions(false); diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index b18aa5d63..32ebe7c61 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -278,7 +278,7 @@ export class LinkFollowBox extends React.Component { alias.width = width; alias.height = height; - this.sourceView.props.addDocument(alias, false); + this.sourceView.props.addDocument(alias); } } diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index a824ae9cc..e4b2ecffd 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -9,7 +9,6 @@ import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { CollectionSchemaView } from "../collections/CollectionSchemaView"; -import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; import { LinkFollowBox } from "../linking/LinkFollowBox"; import { YoutubeBox } from "./../../apis/youtube/YoutubeBox"; @@ -100,7 +99,7 @@ export class DocumentContentsView extends React.Component; + ContainingCollectionView: Opt; ContainingCollectionDoc: Opt; Document: Doc; DataDoc?: Doc; fitToBox?: boolean; onClick?: ScriptField; - addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean; + addDocument?: (doc: Doc) => boolean; removeDocument?: (doc: Doc) => boolean; moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; ScreenToLocalTransform: () => Transform; @@ -217,7 +216,7 @@ export class DocumentView extends DocComponent(Docu let maxLocation = StrCast(this.Document.maximizeLocation, "inPlace"); maxLocation = this.Document.maximizeLocation = (!ctrlKey ? !altKey ? maxLocation : (maxLocation !== "inPlace" ? "inPlace" : "onRight") : (maxLocation !== "inPlace" ? "inPlace" : "inTab")); if (maxLocation === "inPlace") { - expandedDocs.forEach(maxDoc => this.props.addDocument && this.props.addDocument(maxDoc, false)); + expandedDocs.forEach(maxDoc => this.props.addDocument && this.props.addDocument(maxDoc)); let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2); DocumentManager.Instance.animateBetweenPoint(scrpt, expandedDocs); } else { diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index c17730f48..074efaf6c 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -8,7 +8,6 @@ import { List } from "../../../new_fields/List"; import { RichTextField } from "../../../new_fields/RichTextField"; import { AudioField, ImageField, VideoField } from "../../../new_fields/URLField"; import { Transform } from "../../util/Transform"; -import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; import { AudioBox } from "./AudioBox"; import { FormattedTextBox } from "./FormattedTextBox"; @@ -28,7 +27,7 @@ export interface FieldViewProps { fieldExt: string; leaveNativeSize?: boolean; fitToBox?: boolean; - ContainingCollectionView: Opt; + ContainingCollectionView: Opt; ContainingCollectionDoc: Opt; ruleProvider: Doc | undefined; Document: Doc; @@ -37,7 +36,7 @@ export interface FieldViewProps { isSelected: () => boolean; select: (isCtrlPressed: boolean) => void; renderDepth: number; - addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean; + addDocument?: (document: Doc) => boolean; addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean; pinToPres: (document: Doc) => void; removeDocument?: (document: Doc) => boolean; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 1f3887608..57803be1f 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -174,7 +174,7 @@ export class PDFBox extends DocComponent(PdfDocumen ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" }); } - _initialScale: number | undefined; + _initialScale: number | undefined; // the initial scale of the PDF when first rendered which determines whether the document will be live on startup or not. Getting bigger after startup won't make it automatically be live.... render() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.active ? "" : "-interactive"); diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss index d651a8621..b3cd439aa 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -1,6 +1,14 @@ +// .videoBox-container { +// .collectionfreeformview-container { +// mix-blend-mode: multiply; +// } +// } + .videoBox-content-YouTube, .videoBox-content-YouTube-fullScreen, .videoBox-content, .videoBox-content-interactive, .videoBox-cont-fullScreen { width: 100%; + z-index: 0; // logically this should be 0 (or unset) which would give us transparent brush strokes over videos. However, this makes Chrome crawl to a halt + position: absolute; } .videoBox-content, .videoBox-content-interactive, .videoBox-content-fullScreen { @@ -11,7 +19,52 @@ height: 100%; } -.videoBox-content-interactive, .videoBox-content-fullScreen, -.videoBox-content-YouTube-fullScreen { +.videoBox-content-interactive, .videoBox-content-fullScreen, .videoBox-content-YouTube-fullScreen { pointer-events: all; +} + +.videoBox-time{ + color : white; + top :25px; + left : 25px; + position: absolute; + background-color: rgba(50, 50, 50, 0.2); + transform-origin: left top; + pointer-events:all; +} +.videoBox-snapshot{ + color : white; + top :25px; + right : 25px; + position: absolute; + background-color:rgba(50, 50, 50, 0.2); + transform-origin: left top; + pointer-events:all; +} +.videoBox-play { + width: 25px; + height: 20px; + bottom: 25px; + left : 25px; + position: absolute; + color : white; + background-color: rgba(50, 50, 50, 0.2); + border-radius: 4px; + text-align: center; + transform-origin: left bottom; + pointer-events:all; +} +.videoBox-full { + width: 25px; + height: 20px; + bottom: 25px; + right : 25px; + position: absolute; + color : white; + background-color: rgba(50, 50, 50, 0.2); + border-radius: 4px; + text-align: center; + transform-origin: right bottom; + pointer-events:all; + } \ No newline at end of file diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index e83aa8bea..feb067d8f 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -3,11 +3,11 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction, import { observer } from "mobx-react"; import * as rp from 'request-promise'; import { InkTool } from "../../../new_fields/InkField"; -import { makeInterface, createSchema } from "../../../new_fields/Schema"; -import { Cast, FieldValue, NumCast, BoolCast } from "../../../new_fields/Types"; +import { makeInterface, createSchema, listSpec } from "../../../new_fields/Schema"; +import { Cast, FieldValue, NumCast, BoolCast, StrCast } from "../../../new_fields/Types"; import { VideoField } from "../../../new_fields/URLField"; import { RouteStore } from "../../../server/RouteStore"; -import { Utils } from "../../../Utils"; +import { Utils, emptyFunction, returnOne } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; @@ -22,6 +22,8 @@ import { faVideo } from "@fortawesome/free-solid-svg-icons"; import { Doc } from "../../../new_fields/Doc"; import { ScriptField } from "../../../new_fields/ScriptField"; import { positionSchema } from "./CollectionFreeFormDocumentView"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; var path = require('path'); export const timeSchema = createSchema({ @@ -34,19 +36,24 @@ library.add(faVideo); @observer export class VideoBox extends DocComponent(VideoDocument) { + static _youtubeIframeCounter: number = 0; private _reactionDisposer?: IReactionDisposer; private _youtubeReactionDisposer?: IReactionDisposer; private _youtubePlayer: YT.Player | undefined = undefined; private _videoRef: HTMLVideoElement | null = null; private _youtubeIframeId: number = -1; private _youtubeContentCreated = false; - static _youtubeIframeCounter: number = 0; + private _downX: number = 0; + private _downY: number = 0; + private _isResetClick = 0; + private _isChildActive = false; + private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); @observable _forceCreateYouTubeIFrame = false; @observable static _showControls: boolean; @observable _playTimer?: NodeJS.Timeout = undefined; @observable _fullScreen = false; @observable public Playing: boolean = false; - public static LayoutString() { return FieldView.LayoutString(VideoBox); } + public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(VideoBox, "data", fieldExt); } public get player(): HTMLVideoElement | null { return this._videoRef; @@ -123,8 +130,8 @@ export class VideoBox extends DocComponent(VideoD //convert to desired file format var dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png' // if you want to preview the captured image, - let filename = path.basename(encodeURIComponent("snapshot" + this.Document.title + "_" + (this.Document.currentTimecode || 0).toString())); - VideoBox.convertDataUri(dataUrl, filename.replace(/\..*$/, "")).then(returnedFilename => { + let filename = path.basename(encodeURIComponent("snapshot" + StrCast(this.Document.title).replace(/\..*$/, "") + "_" + (this.Document.currentTimecode || 0).toString().replace(/\./, "_"))); + VideoBox.convertDataUri(dataUrl, filename).then(returnedFilename => { if (returnedFilename) { let url = this.choosePath(Utils.prepend(returnedFilename)); let imageSummary = Docs.Create.ImageDocument(url, { @@ -132,7 +139,7 @@ export class VideoBox extends DocComponent(VideoD width: 150, height: height / width * 150, title: "--snapshot" + (this.Document.currentTimecode || 0) + " image-" }); imageSummary.isButton = true; - this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.addDocument && this.props.ContainingCollectionView.props.addDocument(imageSummary, false); + this.props.addDocument && this.props.addDocument(imageSummary); DocUtils.MakeLink({ doc: imageSummary }, { doc: this.props.Document }, "snapshot from " + this.Document.title, "video frame snapshot"); } }); @@ -259,7 +266,73 @@ export class VideoBox extends DocComponent(VideoD }); } + private get uIButtons() { + let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale); + let curTime = NumCast(this.props.Document.currentTimecode); + return ([
+ {"" + Math.round(curTime)} + {" " + Math.round((curTime - Math.trunc(curTime)) * 100)} +
, +
+ +
, + VideoBox._showControls ? (null) : [ +
+ +
, +
+ F +
+ ]]); + } + + @action + onPlayDown = () => { + if (this.Playing) { + this.Pause(); + } else { + this.Play(); + } + } + @action + onFullDown = (e: React.PointerEvent) => { + this.FullScreen(); + e.stopPropagation(); + e.preventDefault(); + } + + @action + onSnapshot = (e: React.PointerEvent) => { + this.Snapshot(); + e.stopPropagation(); + e.preventDefault(); + } + + @action + onResetDown = (e: React.PointerEvent) => { + this.Pause(); + e.stopPropagation(); + this._isResetClick = 0; + document.addEventListener("pointermove", this.onResetMove, true); + document.addEventListener("pointerup", this.onResetUp, true); + InkingControl.Instance.switchTool(InkTool.Eraser); + } + + @action + onResetMove = (e: PointerEvent) => { + this._isResetClick += Math.abs(e.movementX) + Math.abs(e.movementY); + this.Seek(Math.max(0, NumCast(this.props.Document.currentTimecode, 0) + Math.sign(e.movementX) * 0.0333)); + e.stopImmediatePropagation(); + } + @action + onResetUp = (e: PointerEvent) => { + document.removeEventListener("pointermove", this.onResetMove, true); + document.removeEventListener("pointerup", this.onResetUp, true); + InkingControl.Instance.switchTool(InkTool.None); + this._isResetClick < 10 && (this.props.Document.currentTimecode = 0); + } + @computed get fieldExtensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document); } @computed get youtubeContent() { @@ -273,11 +346,95 @@ export class VideoBox extends DocComponent(VideoD >; } + + setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => { + this._setPreviewCursor = func; + } + + @action.bound + removeDocument(doc: Doc): boolean { + Doc.GetProto(doc).annotationOn = undefined; + //TODO This won't create the field if it doesn't already exist + let targetDataDoc = this.fieldExtensionDoc; + let targetField = this.props.fieldExt; + let value = Cast(targetDataDoc[targetField], listSpec(Doc), []); + let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1); + index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1); + index !== -1 && value.splice(index, 1); + return true; + } + // this is called with the document that was dragged and the collection to move it into. + // if the target collection is the same as this collection, then the move will be allowed. + // otherwise, the document being moved must be able to be removed from its container before + // moving it into the target. + @action.bound + moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { + if (Doc.AreProtosEqual(this.props.Document, targetCollection)) { + return true; + } + return this.removeDocument(doc) ? addDocument(doc) : false; + } + + @action.bound + addDocument(doc: Doc): boolean { + Doc.GetProto(doc).annotationOn = this.props.Document; + var curTime = NumCast(this.props.Document.currentTimecode, -1); + curTime !== -1 && (doc.displayTimecode = curTime); + Doc.AddDocToList(this.fieldExtensionDoc, this.props.fieldExt, doc); + return true; + } + whenActiveChanged = (isActive: boolean) => { + this._isChildActive = isActive; + this.props.whenActiveChanged(isActive); + } + active = () => { + return this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; + } + onClick = (e: React.MouseEvent) => { + this._setPreviewCursor && + e.button === 0 && + Math.abs(e.clientX - this._downX) < 3 && + Math.abs(e.clientY - this._downY) < 3 && + this._setPreviewCursor(e.clientX, e.clientY, false); + } + + onPointerDown = (e: React.PointerEvent): void => { + this._downX = e.clientX; + this._downY = e.clientY; + if ((e.button !== 0 || e.altKey) && this.active()) { + this._setPreviewCursor && this._setPreviewCursor(e.clientX, e.clientY, true); + } + } + @computed get annotationLayer() { + return + + } + render() { Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); - return
+ return (
{this.youtubeVideoId ? this.youtubeContent : this.content} -
; + {this.annotationLayer} + {this.uIButtons} +
); } } diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 29eef27a0..bb4c5adc9 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -91,7 +91,7 @@ export class WebBox extends React.Component { }); SelectionManager.SelectedDocuments().map(dv => { - dv.props.addDocument && dv.props.addDocument(newBox, false); + dv.props.addDocument && dv.props.addDocument(newBox); dv.props.removeDocument && dv.props.removeDocument(dv.props.Document); }); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 65ca830d9..366861144 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -18,7 +18,6 @@ import PDFMenu from "./PDFMenu"; import "./PDFViewer.scss"; import React = require("react"); import * as rp from "request-promise"; -import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; import Annotation from "./Annotation"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; @@ -50,10 +49,10 @@ interface IViewerProps { GoToPage?: (n: number) => void; addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean; pinToPres: (document: Doc) => void; - addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean; + addDocument?: (doc: Doc) => boolean; setPdfViewer: (view: PDFViewer) => void; ScreenToLocalTransform: () => Transform; - ContainingCollectionView: Opt; + ContainingCollectionView: Opt; whenActiveChanged: (isActive: boolean) => void; } @@ -582,12 +581,15 @@ export class PDFViewer extends React.Component { } return this.removeDocument(doc) ? addDocument(doc) : false; } - - + @action.bound + addDocument(doc: Doc): boolean { + Doc.GetProto(doc).annotationOn = this.props.Document; + Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); + return true; + } @action.bound removeDocument(doc: Doc): boolean { Doc.GetProto(doc).annotationOn = undefined; - //TODO This won't create the field if it doesn't already exist let targetDataDoc = this.props.fieldExtensionDoc; let targetField = this.props.fieldExt; let value = Cast(targetDataDoc[targetField], listSpec(Doc), []); @@ -659,8 +661,8 @@ export class PDFViewer extends React.Component { whenActiveChanged={this.whenActiveChanged} removeDocument={this.removeDocument} moveDocument={this.moveDocument} - addDocument={(doc: Doc, allow: boolean | undefined) => { Doc.GetProto(doc).annotationOn = this.props.Document; Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); return true; }} - CollectionView={this.props.ContainingCollectionView} + addDocument={this.addDocument} + CollectionView={undefined} ScreenToLocalTransform={this.scrollXf} ruleProvider={undefined} renderDepth={this.props.renderDepth + 1} -- cgit v1.2.3-70-g09d2 From 21aab2fbf28e9cf6d08366b57d368d120d6813bb Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 10 Oct 2019 16:42:12 -0400 Subject: got rid of all CollectionView wrappers around XxxBox's added DocAnnotatableComponent --- src/client/documents/Documents.ts | 4 +- src/client/util/RichTextSchema.tsx | 4 +- src/client/views/DocComponent.tsx | 50 ++++++- .../collections/CollectionMasonryViewFieldRow.tsx | 2 +- .../CollectionStackingViewFieldColumn.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 1 + .../collectionFreeForm/CollectionFreeFormView.tsx | 20 +-- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 8 +- src/client/views/nodes/ImageBox.scss | 8 +- src/client/views/nodes/ImageBox.tsx | 44 +++++-- src/client/views/nodes/PDFBox.tsx | 24 ++-- src/client/views/nodes/VideoBox.scss | 8 +- src/client/views/nodes/VideoBox.tsx | 143 ++++++--------------- src/client/views/nodes/WebBox.tsx | 69 +++++++--- src/client/views/pdf/Annotation.tsx | 8 +- src/client/views/pdf/PDFViewer.tsx | 65 +++------- src/new_fields/Doc.ts | 6 +- 18 files changed, 234 insertions(+), 234 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 22aa74634..0114f82d8 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -120,11 +120,11 @@ export namespace Docs { options: { height: 300, backgroundColor: "black" } }], [DocumentType.IMG, { - layout: { view: ImageBox, collectionView: [CollectionView, data, anno] as CollectionViewType }, + layout: { view: ImageBox, ext: anno }, options: {} }], [DocumentType.WEB, { - layout: { view: WebBox, collectionView: [CollectionView, data, anno] as CollectionViewType }, + layout: { view: WebBox, ext: anno }, options: { height: 300 } }], [DocumentType.COL, { diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index a5502577b..063686d58 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -784,8 +784,8 @@ export class DashDocView { addDocTab={self._textBox.props.addDocTab} pinToPres={returnFalse} renderDepth={1} - PanelWidth={self._dashDoc![WidthSym]} - PanelHeight={self._dashDoc![HeightSym]} + PanelWidth={self._dashDoc[WidthSym]} + PanelHeight={self._dashDoc[HeightSym]} focus={emptyFunction} backgroundColor={returnEmptyString} parentActive={returnFalse} diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index d6562492f..93e852bef 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -1,6 +1,8 @@ import * as React from 'react'; import { Doc } from '../../new_fields/Doc'; -import { computed } from 'mobx'; +import { computed, action } from 'mobx'; +import { Cast } from '../../new_fields/Types'; +import { listSpec } from '../../new_fields/Schema'; export function DocComponent

(schemaCtor: (doc: Doc) => T) { class Component extends React.Component

{ @@ -11,4 +13,50 @@ export function DocComponent

(schemaCtor: (doc: D } } return Component; +} + +interface DocAnnotatableProps { + Document: Doc; + DataDoc?: Doc; + fieldKey: string; + fieldExt: string; + whenActiveChanged: (isActive: boolean) => void; + isSelected: () => boolean, renderDepth: number +} +export function DocAnnotatableComponent

(schemaCtor: (doc: Doc) => T) { + class Component extends React.Component

{ + //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then + @computed + get Document(): T { + return schemaCtor(this.props.Document); + } + _isChildActive = false; + @action.bound + removeDocument(doc: Doc): boolean { + Doc.GetProto(doc).annotationOn = undefined; + let value = Cast(this.extensionDoc[this.props.fieldExt], listSpec(Doc), []); + let index = value ? Doc.IndexOf(doc, value.map(d => d as Doc), true) : -1; + return index !== -1 && value.splice(index, 1) ? true : false; + } + + @computed get dataDoc() { return (this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; } + + @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } + + // if the moved document is already in this overlay collection nothing needs to be done. + // otherwise, if the document can be removed from where it was, it will then be added to this document's overlay collection. + @action.bound + moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { + return Doc.AreProtosEqual(this.props.Document, targetCollection) ? true : this.removeDocument(doc) ? addDocument(doc) : false; + } + + @action.bound + addDocument(doc: Doc): boolean { + Doc.GetProto(doc).annotationOn = this.props.Document; + return Doc.AddDocToList(this.extensionDoc, this.props.fieldExt, doc); + } + whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive); + active = () => this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; + } + return Component; } \ No newline at end of file diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 5c1960d53..6251d7114 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -86,7 +86,7 @@ export class CollectionMasonryViewFieldRow extends React.Component { let parsed = parseInt(value); diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index e8627780d..815b28586 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -72,7 +72,7 @@ export class CollectionStackingViewFieldColumn extends React.Component { let parsed = parseInt(value); if (!isNaN(parsed)) { diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 689adc375..06d048383 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -38,6 +38,7 @@ export interface CollectionViewProps extends FieldViewProps { export interface SubCollectionViewProps extends CollectionViewProps { CollectionView: Opt; ruleProvider: Doc | undefined; + children?: never | (() => JSX.Element[]) | React.ReactNode; } export function CollectionSubView(schemaCtor: (doc: Doc) => T) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d2644480c..adbad5da5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,6 +1,6 @@ import { library } from "@fortawesome/fontawesome-svg-core"; import { faEye } from "@fortawesome/free-regular-svg-icons"; -import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload, faFileUpload } from "@fortawesome/free-solid-svg-icons"; +import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc"; @@ -286,7 +286,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action onPointerMove = (e: PointerEvent): void => { - if (!e.cancelBubble && !this.isAnnotationOverlay) { + if (!e.cancelBubble) { if (this._hitCluster && this.tryDragCluster(e)) { e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers e.preventDefault(); @@ -339,7 +339,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action onPointerWheel = (e: React.WheelEvent): void => { - if (this.props.Document.lockedPosition || this.props.Document.inOverlay || this.isAnnotationOverlay) return; + if (this.props.Document.lockedPosition || this.props.Document.inOverlay) return; if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming e.stopPropagation(); } @@ -477,7 +477,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { PanelWidth: layoutDoc[WidthSym], PanelHeight: layoutDoc[HeightSym], ContentScaling: returnOne, - ContainingCollectionView: this.props.CollectionView, + ContainingCollectionView: this.props.ContainingCollectionView, focus: this.focusDocument, backgroundColor: returnEmptyString, parentActive: this.props.active, @@ -681,10 +681,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } - private childViews = () => [ - , - ...this.views - ] + private childViews = () => { + let children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : []; + return [ + , + ...children, + ...this.views, + ]; + } render() { // update the actual dimensions of the collection so that they can inquired (e.g., by a minimap) this.props.Document.fitX = this.contentBounds && this.contentBounds.x; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index bb787106c..ecdd02b0f 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -217,7 +217,7 @@ export class MarqueeView extends React.Component this._downY = y; PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument); } - }) + }); @action onClick = (e: React.MouseEvent): void => { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 9ca77cc8b..cbe4945af 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -218,13 +218,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight); const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true }); let res = terms.map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term)); - let tr = this._editorView!.state.tr; + let tr = this._editorView.state.tr; let flattened: TextSelection[] = []; res.map(r => r.map(h => flattened.push(h))); let lastSel = Math.min(flattened.length - 1, this._searchIndex); flattened.forEach((h: TextSelection, ind: number) => tr = tr.addMark(h.from, h.to, ind === lastSel ? activeMark : mark)); this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; - this._editorView!.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(flattened[lastSel].from), tr.doc.resolve(flattened[lastSel].to))).scrollIntoView()); + this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(flattened[lastSel].from), tr.doc.resolve(flattened[lastSel].to))).scrollIntoView()); } } @@ -232,8 +232,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (this._editorView && (this._editorView as any).docView) { const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight); const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true }); - let end = this._editorView!.state.doc.nodeSize - 2; - this._editorView!.dispatch(this._editorView!.state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark)); + let end = this._editorView.state.doc.nodeSize - 2; + this._editorView.dispatch(this._editorView.state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark)); } } setAnnotation = (start: number, end: number, mark: Mark, opened: boolean, keep: boolean = false) => { diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 71d718b39..97d858f58 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -1,9 +1,9 @@ -.imageBox-cont { +.imageBox-cont, .imageBox-cont-interactive { padding: 0vw; - position: relative; + position: absolute; text-align: center; width: 100%; - height: auto; + height: 100%; max-width: 100%; max-height: 100%; pointer-events: none; @@ -11,8 +11,6 @@ .imageBox-cont-interactive { pointer-events: all; - width: 100%; - height: auto; } .imageBox-dot { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index a198a0764..9f39eccea 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -13,20 +13,21 @@ import { ComputedField } from '../../../new_fields/ScriptField'; import { BoolCast, Cast, FieldValue, NumCast, StrCast } from '../../../new_fields/Types'; import { AudioField, ImageField } from '../../../new_fields/URLField'; import { RouteStore } from '../../../server/RouteStore'; -import { Utils } from '../../../Utils'; +import { Utils, returnOne, emptyFunction } from '../../../Utils'; import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from "../../views/ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; -import { DocComponent } from '../DocComponent'; +import { DocAnnotatableComponent } from '../DocComponent'; import { InkingControl } from '../InkingControl'; import { documentSchema } from './DocumentView'; import FaceRectangles from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; import React = require("react"); +import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; var requestImageSize = require('../../util/request-image-size'); var path = require('path'); const { Howl } = require('howler'); @@ -54,9 +55,10 @@ type ImageDocument = makeInterface<[typeof pageSchema, typeof documentSchema]>; const ImageDocument = makeInterface(pageSchema, documentSchema); @observer -export class ImageBox extends DocComponent(ImageDocument) { +export class ImageBox extends DocAnnotatableComponent(ImageDocument) { - public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(ImageBox, fieldKey); } + public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(ImageBox, "data", fieldExt); } + @observable static _showControls: boolean; private _imgRef: React.RefObject = React.createRef(); private _downX: number = 0; private _downY: number = 0; @@ -65,10 +67,6 @@ export class ImageBox extends DocComponent(ImageD private dropDisposer?: DragManager.DragDropDisposer; @observable private hoverActive = false; - @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } - - @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document); } - protected createDropTarget = (ele: HTMLDivElement) => { if (this.dropDisposer) { this.dropDisposer(); @@ -381,7 +379,8 @@ export class ImageBox extends DocComponent(ImageD return (null); } - render() { + @computed + get content() { // let transform = this.props.ScreenToLocalTransform().inverse(); let pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50; // var [sptX, sptY] = transform.transformPoint(0, 0); @@ -393,7 +392,6 @@ export class ImageBox extends DocComponent(ImageD let paths: string[] = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")]; // this._curSuffix = ""; // if (w > 20) { - Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); let alts = DocListCast(this.extensionDoc.Alternates); let altpaths: string[] = alts.filter(doc => doc.data instanceof ImageField).map(doc => this.choosePath((doc.data as ImageField).url)); let field = this.dataDoc[this.props.fieldKey]; @@ -448,4 +446,30 @@ export class ImageBox extends DocComponent(ImageD

); } + + render() { + Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); + return (
+ + {() => [this.content]} + +
); + } } \ No newline at end of file diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 57803be1f..9af6d7cad 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,17 +1,21 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction, untracked, trace } from 'mobx'; +import { action, observable, runInAction } from 'mobx'; import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; import 'react-image-lightbox/style.css'; -import { Doc, Opt, WidthSym } from "../../../new_fields/Doc"; +import { Opt, WidthSym } from "../../../new_fields/Doc"; import { makeInterface } from "../../../new_fields/Schema"; import { ScriptField } from '../../../new_fields/ScriptField'; -import { Cast, NumCast } from "../../../new_fields/Types"; +import { Cast } from "../../../new_fields/Types"; import { PdfField } from "../../../new_fields/URLField"; +import { Utils } from '../../../Utils'; import { KeyCodes } from '../../northstar/utils/KeyCodes'; +import { undoBatch } from '../../util/UndoManager'; import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeFormView'; -import { DocComponent } from "../DocComponent"; +import { ContextMenu } from '../ContextMenu'; +import { ContextMenuProps } from '../ContextMenuItem'; +import { DocAnnotatableComponent } from "../DocComponent"; import { InkingControl } from "../InkingControl"; import { PDFViewer } from "../pdf/PDFViewer"; import { documentSchema } from "./DocumentView"; @@ -19,22 +23,17 @@ import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); -import { undoBatch } from '../../util/UndoManager'; -import { ContextMenuProps } from '../ContextMenuItem'; -import { ContextMenu } from '../ContextMenu'; -import { Utils } from '../../../Utils'; type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>; const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @observer -export class PDFBox extends DocComponent(PdfDocument) { +export class PDFBox extends DocAnnotatableComponent(PdfDocument) { public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(PDFBox, "data", fieldExt); } private _keyValue: string = ""; private _valueValue: string = ""; private _scriptValue: string = ""; private _searchString: string = ""; - private _isChildActive = false; private _everActive = false; // has this box ever had its contents activated -- if so, stop drawing the overlay title private _pdfViewer: PDFViewer | undefined; private _keyRef: React.RefObject = React.createRef(); @@ -46,9 +45,6 @@ export class PDFBox extends DocComponent(PdfDocumen @observable private _pdf: Opt; @observable private _pageControls = false; - @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } - @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document); } - componentDidMount() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); if (pdfUrl instanceof PdfField) { @@ -206,7 +202,7 @@ export class PDFBox extends DocComponent(PdfDocumen pinToPres={this.props.pinToPres} addDocument={this.props.addDocument} ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select} isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged} - fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} startupLive={this._initialScale < 2.5 ? true : false} /> + fieldKey={this.props.fieldKey} extensionDoc={this.extensionDoc} startupLive={this._initialScale < 2.5 ? true : false} /> {this.settingsPanel()}
); } diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss index b3cd439aa..48623eaaf 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -1,8 +1,6 @@ -// .videoBox-container { -// .collectionfreeformview-container { -// mix-blend-mode: multiply; -// } -// } +.videoBox-container { + pointer-events: all; +} .videoBox-content-YouTube, .videoBox-content-YouTube-fullScreen, .videoBox-content, .videoBox-content-interactive, .videoBox-cont-fullScreen { diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index feb067d8f..aa9b28118 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -11,7 +11,7 @@ import { Utils, emptyFunction, returnOne } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; -import { DocComponent } from "../DocComponent"; +import { DocAnnotatableComponent } from "../DocComponent"; import { DocumentDecorations } from "../DocumentDecorations"; import { InkingControl } from "../InkingControl"; import { documentSchema } from "./DocumentView"; @@ -35,7 +35,7 @@ const VideoDocument = makeInterface(documentSchema, positionSchema, timeSchema); library.add(faVideo); @observer -export class VideoBox extends DocComponent(VideoDocument) { +export class VideoBox extends DocAnnotatableComponent(VideoDocument) { static _youtubeIframeCounter: number = 0; private _reactionDisposer?: IReactionDisposer; private _youtubeReactionDisposer?: IReactionDisposer; @@ -43,16 +43,12 @@ export class VideoBox extends DocComponent(VideoD private _videoRef: HTMLVideoElement | null = null; private _youtubeIframeId: number = -1; private _youtubeContentCreated = false; - private _downX: number = 0; - private _downY: number = 0; private _isResetClick = 0; - private _isChildActive = false; - private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); @observable _forceCreateYouTubeIFrame = false; - @observable static _showControls: boolean; @observable _playTimer?: NodeJS.Timeout = undefined; @observable _fullScreen = false; - @observable public Playing: boolean = false; + @observable _playing = false; + @observable static _showControls: boolean; public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(VideoBox, "data", fieldExt); } public get player(): HTMLVideoElement | null { @@ -72,7 +68,7 @@ export class VideoBox extends DocComponent(VideoD } @action public Play = (update: boolean = true) => { - this.Playing = true; + this._playing = true; update && this.player && this.player.play(); update && this._youtubePlayer && this._youtubePlayer.playVideo(); this._youtubePlayer && !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 5)); @@ -85,7 +81,7 @@ export class VideoBox extends DocComponent(VideoD } @action public Pause = (update: boolean = true) => { - this.Playing = false; + this._playing = false; update && this.player && this.player.pause(); update && this._youtubePlayer && this._youtubePlayer.pauseVideo && this._youtubePlayer.pauseVideo(); this._youtubePlayer && this._playTimer && clearInterval(this._playTimer); @@ -181,7 +177,7 @@ export class VideoBox extends DocComponent(VideoD vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen); this._reactionDisposer && this._reactionDisposer(); this._reactionDisposer = reaction(() => this.Document.currentTimecode || 0, - time => !this.Playing && (vref.currentTime = time), { fireImmediately: true }); + time => !this._playing && (vref.currentTime = time), { fireImmediately: true }); } } @@ -218,7 +214,7 @@ export class VideoBox extends DocComponent(VideoD let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive"; let style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive; return !field ?
Loading
: -
, VideoBox._showControls ? (null) : [
- +
, -
+
F
]]); } @action - onPlayDown = () => { - if (this.Playing) { - this.Pause(); - } else { - this.Play(); - } - } + onPlayDown = () => this._playing ? this.Pause() : this.Play() @action onFullDown = (e: React.PointerEvent) => { @@ -342,97 +332,40 @@ export class VideoBox extends DocComponent(VideoD let start = untracked(() => Math.round(this.Document.currentTimecode || 0)); return ; - } - - - setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => { - this._setPreviewCursor = func; + src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=1&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`} />; } @action.bound - removeDocument(doc: Doc): boolean { - Doc.GetProto(doc).annotationOn = undefined; - //TODO This won't create the field if it doesn't already exist - let targetDataDoc = this.fieldExtensionDoc; - let targetField = this.props.fieldExt; - let value = Cast(targetDataDoc[targetField], listSpec(Doc), []); - let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1); - index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1); - index !== -1 && value.splice(index, 1); - return true; - } - // this is called with the document that was dragged and the collection to move it into. - // if the target collection is the same as this collection, then the move will be allowed. - // otherwise, the document being moved must be able to be removed from its container before - // moving it into the target. - @action.bound - moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { - if (Doc.AreProtosEqual(this.props.Document, targetCollection)) { - return true; - } - return this.removeDocument(doc) ? addDocument(doc) : false; - } - - @action.bound - addDocument(doc: Doc): boolean { + addDocumentWithTimestamp(doc: Doc): boolean { Doc.GetProto(doc).annotationOn = this.props.Document; var curTime = NumCast(this.props.Document.currentTimecode, -1); curTime !== -1 && (doc.displayTimecode = curTime); - Doc.AddDocToList(this.fieldExtensionDoc, this.props.fieldExt, doc); - return true; - } - whenActiveChanged = (isActive: boolean) => { - this._isChildActive = isActive; - this.props.whenActiveChanged(isActive); - } - active = () => { - return this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; - } - onClick = (e: React.MouseEvent) => { - this._setPreviewCursor && - e.button === 0 && - Math.abs(e.clientX - this._downX) < 3 && - Math.abs(e.clientY - this._downY) < 3 && - this._setPreviewCursor(e.clientX, e.clientY, false); - } - - onPointerDown = (e: React.PointerEvent): void => { - this._downX = e.clientX; - this._downY = e.clientY; - if ((e.button !== 0 || e.altKey) && this.active()) { - this._setPreviewCursor && this._setPreviewCursor(e.clientX, e.clientY, true); - } - } - @computed get annotationLayer() { - return - + return Doc.AddDocToList(this.fieldExtensionDoc, this.props.fieldExt, doc); } render() { Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); - return (
- {this.youtubeVideoId ? this.youtubeContent : this.content} - {this.annotationLayer} + return (
+ + {() => [this.youtubeVideoId ? this.youtubeContent : this.content]} + {this.uIButtons}
); } diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index bb4c5adc9..7c7f9fb83 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -1,34 +1,36 @@ +import { library } from "@fortawesome/fontawesome-svg-core"; +import { faStickyNote } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { FieldResult, Doc, Field } from "../../../new_fields/Doc"; +import { Doc, FieldResult } from "../../../new_fields/Doc"; import { HtmlField } from "../../../new_fields/HtmlField"; +import { InkTool } from "../../../new_fields/InkField"; +import { makeInterface } from "../../../new_fields/Schema"; +import { Cast, NumCast } from "../../../new_fields/Types"; import { WebField } from "../../../new_fields/URLField"; +import { emptyFunction, returnOne, Utils } from "../../../Utils"; +import { Docs } from "../../documents/Documents"; +import { SelectionManager } from "../../util/SelectionManager"; +import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { DocumentDecorations } from "../DocumentDecorations"; import { InkingControl } from "../InkingControl"; import { FieldView, FieldViewProps } from './FieldView'; +import { KeyValueBox } from "./KeyValueBox"; import "./WebBox.scss"; import React = require("react"); -import { InkTool } from "../../../new_fields/InkField"; -import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; -import { Utils } from "../../../Utils"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faStickyNote } from '@fortawesome/free-solid-svg-icons'; -import { observable, action, computed } from "mobx"; -import { listSpec } from "../../../new_fields/Schema"; -import { RefField } from "../../../new_fields/RefField"; -import { ObjectField } from "../../../new_fields/ObjectField"; -import { updateSourceFile } from "typescript"; -import { KeyValueBox } from "./KeyValueBox"; -import { setReactionScheduler } from "mobx/lib/internal"; -import { library } from "@fortawesome/fontawesome-svg-core"; -import { SelectionManager } from "../../util/SelectionManager"; -import { Docs } from "../../documents/Documents"; +import { documentSchema } from "./DocumentView"; +import { DocAnnotatableComponent } from "../DocComponent"; library.add(faStickyNote); +type WebDocument = makeInterface<[typeof documentSchema]>; +const WebDocument = makeInterface(documentSchema); + @observer -export class WebBox extends React.Component { +export class WebBox extends DocAnnotatableComponent(WebDocument) { - public static LayoutString() { return FieldView.LayoutString(WebBox); } + public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(WebBox, "data", fieldExt); } @observable private collapsed: boolean = true; @observable private url: string = ""; @@ -162,8 +164,10 @@ export class WebBox extends React.Component { e.stopPropagation(); } } - render() { - let field = this.props.Document[this.props.fieldKey]; + + @computed + get content() { + let field = this.dataDoc[this.props.fieldKey]; let view; if (field instanceof HtmlField) { view = ; @@ -189,4 +193,29 @@ export class WebBox extends React.Component { {!frozen ? (null) :
} ); } + render() { + Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); + return (
+ + {() => [this.content]} + +
); + } } \ No newline at end of file diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 5a07b88d9..ad6240c70 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -11,7 +11,7 @@ import "./Annotation.scss"; interface IAnnotationProps { anno: Doc; - fieldExtensionDoc: Doc; + extensionDoc: Doc; addDocTab: (document: Doc, dataDoc: Opt, where: string) => boolean; pinToPres: (document: Doc) => void; focus: (doc: Doc) => void; @@ -29,7 +29,7 @@ interface IRegionAnnotationProps { y: number; width: number; height: number; - fieldExtensionDoc: Doc; + extensionDoc: Doc; addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean; pinToPres: (document: Doc) => void; document: Doc; @@ -66,12 +66,12 @@ class RegionAnnotation extends React.Component { } deleteAnnotation = () => { - let annotation = DocListCast(this.props.fieldExtensionDoc.annotations); + let annotation = DocListCast(this.props.extensionDoc.annotations); let group = FieldValue(Cast(this.props.document.group, Doc)); if (group) { if (annotation.indexOf(group) !== -1) { let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc))); - this.props.fieldExtensionDoc.annotations = new List(newAnnotations); + this.props.extensionDoc.annotations = new List(newAnnotations); } DocListCast(group.annotations).forEach(anno => anno.delete = true); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 366861144..d0759d207 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -6,7 +6,7 @@ import { Dictionary } from "typescript-collections"; import { Doc, DocListCast, FieldResult, WidthSym, Opt, HeightSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; -import { listSpec } from "../../../new_fields/Schema"; +import { makeInterface } from "../../../new_fields/Schema"; import { ScriptField } from "../../../new_fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { smoothScroll, Utils, emptyFunction, returnOne, intersectRect, addStyleSheet, addStyleSheetRule, clearStyleSheetRules } from "../../../Utils"; @@ -23,19 +23,24 @@ import Annotation from "./Annotation"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { SelectionManager } from "../../util/SelectionManager"; import { undoBatch } from "../../util/UndoManager"; +import { DocAnnotatableComponent } from "../DocComponent"; +import { documentSchema } from "../nodes/DocumentView"; const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); const pdfjsLib = require("pdfjs-dist"); pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`; +type PdfDocument = makeInterface<[typeof documentSchema]>; +const PdfDocument = makeInterface(documentSchema); interface IViewerProps { pdf: Pdfjs.PDFDocumentProxy; url: string; - Document: Doc; - DataDoc?: Doc; - fieldExtensionDoc: Doc; fieldKey: string; fieldExt: string; + Document: Doc; + DataDoc?: Doc; + ContainingCollectionView: Opt; + extensionDoc: Doc; PanelWidth: () => number; PanelHeight: () => number; ContentScaling: () => number; @@ -52,7 +57,6 @@ interface IViewerProps { addDocument?: (doc: Doc) => boolean; setPdfViewer: (view: PDFViewer) => void; ScreenToLocalTransform: () => Transform; - ContainingCollectionView: Opt; whenActiveChanged: (isActive: boolean) => void; } @@ -60,7 +64,7 @@ interface IViewerProps { * Handles rendering and virtualization of the pdf */ @observer -export class PDFViewer extends React.Component { +export class PDFViewer extends DocAnnotatableComponent(PdfDocument) { static _annotationStyle: any = addStyleSheet(); @observable private _pageSizes: { width: number, height: number }[] = []; @observable private _annotations: Doc[] = []; @@ -79,7 +83,6 @@ export class PDFViewer extends React.Component { private _pdfViewer: any; private _retries = 0; // number of times tried to create the PDF viewer - private _isChildActive = false; private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); private _annotationLayer: React.RefObject = React.createRef(); private _reactionDisposer?: IReactionDisposer; @@ -97,7 +100,7 @@ export class PDFViewer extends React.Component { private _coverPath: any; @computed get allAnnotations() { - return DocListCast(this.props.fieldExtensionDoc.annotations).filter( + return DocListCast(this.props.extensionDoc.annotations).filter( anno => this._script.run({ this: anno }, console.log, true).result); } @@ -186,7 +189,7 @@ export class PDFViewer extends React.Component { await this.initialLoad(); this._annotationReactionDisposer = reaction( - () => this.props.fieldExtensionDoc && DocListCast(this.props.fieldExtensionDoc.annotations), + () => this.props.extensionDoc && DocListCast(this.props.extensionDoc.annotations), annotations => annotations && annotations.length && this.renderAnnotations(annotations, true), { fireImmediately: true }); @@ -530,7 +533,7 @@ export class PDFViewer extends React.Component { highlight = (color: string) => { // creates annotation documents for current highlights let annotationDoc = this.makeAnnotationDocument(color); - annotationDoc && Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, annotationDoc); + annotationDoc && Doc.AddDocToList(this.props.extensionDoc, this.props.fieldExt, annotationDoc); return annotationDoc; } @@ -570,40 +573,9 @@ export class PDFViewer extends React.Component { DragManager.StartDocumentDrag([], new DragManager.DocumentDragData([view]), 0, 0); } - // this is called with the document that was dragged and the collection to move it into. - // if the target collection is the same as this collection, then the move will be allowed. - // otherwise, the document being moved must be able to be removed from its container before - // moving it into the target. - @action.bound - moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { - if (Doc.AreProtosEqual(this.props.Document, targetCollection)) { - return true; - } - return this.removeDocument(doc) ? addDocument(doc) : false; - } - @action.bound - addDocument(doc: Doc): boolean { - Doc.GetProto(doc).annotationOn = this.props.Document; - Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); - return true; - } - @action.bound - removeDocument(doc: Doc): boolean { - Doc.GetProto(doc).annotationOn = undefined; - let targetDataDoc = this.props.fieldExtensionDoc; - let targetField = this.props.fieldExt; - let value = Cast(targetDataDoc[targetField], listSpec(Doc), []); - let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1); - index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1); - index !== -1 && value.splice(index, 1); - return true; - } scrollXf = () => { return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this._scrollTop) : this.props.ScreenToLocalTransform(); } - setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => { - this._setPreviewCursor = func; - } onClick = (e: React.MouseEvent) => { this._setPreviewCursor && e.button === 0 && @@ -611,13 +583,9 @@ export class PDFViewer extends React.Component { Math.abs(e.clientY - this._downY) < 3 && this._setPreviewCursor(e.clientX, e.clientY, false); } - whenActiveChanged = (isActive: boolean) => { - this._isChildActive = isActive; - this.props.whenActiveChanged(isActive); - } - active = () => { - return this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; - } + + setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func; + getCoverImage = () => { if (!this.props.Document[HeightSym]() || !this.props.Document.nativeHeight) { @@ -632,7 +600,6 @@ export class PDFViewer extends React.Component { style={{ position: "absolute", display: "inline-block", top: 0, left: 0, width: `${nativeWidth}px`, height: `${nativeHeight}px` }} />; } - @action onZoomWheel = (e: React.WheelEvent) => { e.stopPropagation(); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 418863bcc..eb752f8c6 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -332,8 +332,10 @@ export namespace Doc { return Array.from(results); } - export function IndexOf(toFind: Doc, list: Doc[]) { - return list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind)); + export function IndexOf(toFind: Doc, list: Doc[], allowProtos: boolean = true) { + let index = list.reduce((p, v, i) => (v instanceof Doc && v === toFind) ? i : p, -1); + index = allowProtos && index !== -1 ? index : list.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, toFind)) ? i : p, -1); + return index; // list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind)); } export function RemoveDocFromList(listDoc: Doc, key: string, doc: Doc) { if (listDoc[key] === undefined) { -- cgit v1.2.3-70-g09d2 From a73a72586c72cd620c16dd7bc0baad88c8e49a34 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 11 Oct 2019 01:38:48 -0400 Subject: switching search around to left-hand side. --- src/client/documents/DocumentTypes.ts | 3 +- src/client/documents/Documents.ts | 10 +++++ src/client/views/GlobalKeyHandler.ts | 4 ++ src/client/views/MainView.tsx | 10 +++-- .../views/collections/CollectionTreeView.tsx | 5 ++- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/QueryBox.scss | 0 src/client/views/nodes/QueryBox.tsx | 50 ++++++++++++++++++++++ src/client/views/search/FilterBox.tsx | 4 +- src/client/views/search/IconBar.tsx | 14 ------ src/client/views/search/SearchBox.scss | 17 ++++++-- src/client/views/search/SearchBox.tsx | 2 - src/client/views/search/SearchItem.scss | 39 +++++++---------- src/client/views/search/SearchItem.tsx | 19 +++----- .../authentication/models/current_user_utils.ts | 6 +++ 15 files changed, 120 insertions(+), 66 deletions(-) create mode 100644 src/client/views/nodes/QueryBox.scss create mode 100644 src/client/views/nodes/QueryBox.tsx (limited to 'src/client/documents') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index e5d5885cd..7abaa4043 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -20,5 +20,6 @@ export enum DocumentType { DRAGBOX = "dragbox", PRES = "presentation", LINKFOLLOW = "linkfollow", - PRESELEMENT = "preselement" + PRESELEMENT = "preselement", + QUERY = "search", } \ No newline at end of file diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 0114f82d8..7f6ab50d8 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -45,6 +45,7 @@ import { ProxyField } from "../../new_fields/Proxy"; import { DocumentType } from "./DocumentTypes"; import { LinkFollowBox } from "../views/linking/LinkFollowBox"; import { PresElementBox } from "../views/presentationview/PresElementBox"; +import { QueryBox } from "../views/nodes/QueryBox"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -62,6 +63,7 @@ export interface DocumentOptions { panY?: number; page?: number; scale?: number; + fitWidth?: boolean; layout?: string | Doc; isTemplate?: boolean; templates?: List; @@ -119,6 +121,10 @@ export namespace Docs { layout: { view: HistogramBox, collectionView: [CollectionView, data] as CollectionViewType }, options: { height: 300, backgroundColor: "black" } }], + [DocumentType.QUERY, { + layout: { view: QueryBox }, + options: { width: 400, fitWidth: true } + }], [DocumentType.IMG, { layout: { view: ImageBox, ext: anno }, options: {} @@ -374,6 +380,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.HIST), new HistogramField(histoOp), options); } + export function QueryDocument(options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocumentType.QUERY), "", options); + } + export function TextDocument(options: DocumentOptions = {}) { return InstanceFromProto(Prototypes.get(DocumentType.TEXT), "", options); } diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index c519991a5..6815ff926 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -7,6 +7,8 @@ import { action, runInAction } from "mobx"; import { Doc } from "../../new_fields/Doc"; import { DictationManager } from "../util/DictationManager"; import SharingManager from "../util/SharingManager"; +import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; +import { Cast, PromiseValue } from "../../new_fields/Types"; const modifiers = ["control", "meta", "shift", "alt"]; type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise; @@ -162,6 +164,8 @@ export default class KeyManager { break; case "f": MainView.Instance.isSearchVisible = !MainView.Instance.isSearchVisible; + MainView.Instance.flyoutWidth = MainView.Instance.isSearchVisible ? 400 : 0; + PromiseValue(Cast(CurrentUserUtils.UserDocument.searchBox, Doc)).then(pv => pv && (pv.treeViewOpen = (MainView.Instance.flyoutWidth > 0))); break; case "o": let target = SelectionManager.SelectedDocuments()[0]; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index ba4224875..1ec21f638 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -12,7 +12,7 @@ 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 { BoolCast, Cast, FieldValue, StrCast } from '../../new_fields/Types'; +import { BoolCast, Cast, FieldValue, StrCast, PromiseValue } from '../../new_fields/Types'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { RouteStore } from '../../server/RouteStore'; import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils } from '../../Utils'; @@ -463,7 +463,7 @@ export class MainView extends React.Component { return (null); } return
: null, + //this.isSearchVisible ?
: null, ]; } @@ -648,7 +648,9 @@ export class MainView extends React.Component { @observable isSearchVisible = false; @action.bound toggleSearch = () => { - this.isSearchVisible = !this.isSearchVisible; + this.isSearchVisible = !MainView.Instance.isSearchVisible; + MainView.Instance.flyoutWidth = MainView.Instance.isSearchVisible ? 400 : 0; + PromiseValue(Cast(CurrentUserUtils.UserDocument.searchBox, Doc)).then(pv => pv && (pv.treeViewOpen = (MainView.Instance.flyoutWidth > 0))); } @computed private get dictationOverlay() { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 37eb151b1..6f5587b5a 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -260,7 +260,10 @@ class TreeView extends React.Component { let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth); if (aspect) return this.docWidth() * aspect; if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x); - return NumCast(this.props.document.height) ? NumCast(this.props.document.height) : 50; + return this.props.document.fitWidth ? (!this.props.document.nativeHeight ? NumCast(this.props.containingCollection.height) : + Math.min(this.docWidth() * NumCast(this.props.document.scrollHeight, NumCast(this.props.document.nativeHeight)) / NumCast(this.props.document.nativeWidth, + NumCast(this.props.containingCollection.height)))) : + NumCast(this.props.document.height) ? NumCast(this.props.document.height) : 50; })()); } diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index e4b2ecffd..d4e7c6d4e 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -24,6 +24,7 @@ import { ImageBox } from "./ImageBox"; import { KeyValueBox } from "./KeyValueBox"; import { PDFBox } from "./PDFBox"; import { PresBox } from "./PresBox"; +import { QueryBox } from "./QueryBox"; import { PresElementBox } from "../presentationview/PresElementBox"; import { VideoBox } from "./VideoBox"; import { WebBox } from "./WebBox"; @@ -99,7 +100,7 @@ export class DocumentContentsView extends React.Component { + public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(QueryBox, fieldKey); } + _docListChangedReaction: IReactionDisposer | undefined; + componentDidMount() { + } + + componentWillUnmount() { + this._docListChangedReaction && this._docListChangedReaction(); + } + + render() { + return
+ +
+ } +} \ No newline at end of file diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx index da733d64b..b841190d4 100644 --- a/src/client/views/search/FilterBox.tsx +++ b/src/client/views/search/FilterBox.tsx @@ -33,7 +33,7 @@ export enum Keys { export class FilterBox extends React.Component { static Instance: FilterBox; - public _allIcons: string[] = [DocumentType.AUDIO, DocumentType.COL, DocumentType.HIST, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.TEXT, DocumentType.VID, DocumentType.WEB]; + public _allIcons: string[] = [DocumentType.AUDIO, DocumentType.COL, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.TEXT, DocumentType.VID, DocumentType.WEB]; //if true, any keywords can be used. if false, all keywords are required. //this also serves as an indicator if the word status filter is applied @@ -393,7 +393,7 @@ export class FilterBox extends React.Component {
- {this.getActiveFilters()} + {/* {this.getActiveFilters()} */}
{this._filterOpen ? (
diff --git a/src/client/views/search/IconBar.tsx b/src/client/views/search/IconBar.tsx index c9924222f..bdeb57d5c 100644 --- a/src/client/views/search/IconBar.tsx +++ b/src/client/views/search/IconBar.tsx @@ -59,23 +59,9 @@ export class IconBar extends React.Component { render() { return (
-
-
- -
-
Select All
-
{FilterBox.Instance._allIcons.map((type: string) => )} -
-
- -
-
Clear
-
); } diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss index 0dd4d3dc5..bc11604a5 100644 --- a/src/client/views/search/SearchBox.scss +++ b/src/client/views/search/SearchBox.scss @@ -1,6 +1,15 @@ @import "../globalCssVariables"; @import "./NaviconButton.scss"; +.searchBox-container { + display: flex; + flex-direction: column; + width:100%; + height:100%; + position: absolute; + font-size: 10px; + line-height: 1; +} .searchBox-bar { height: 32px; display: flex; @@ -49,17 +58,17 @@ } .searchBox-quickFilter { - width: 500px; - margin-left: 25px; + width: 100%; + height: 40px; margin-top: 10px; } .searchBox-results { - margin-right: 136px; + display:flex; + flex-direction: column; top: 300px; display: flex; flex-direction: column; - margin-right: 72px; // height: 560px; height: 100%; // overflow: hidden; diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index be75a29e0..62c8c255e 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -346,8 +346,6 @@ export class SearchBox extends React.Component { className="searchBox-barChild searchBox-input" onPointerDown={this.openSearch} onKeyPress={this.enter} onFocus={this.openSearch} style={{ width: this._searchbarOpen ? "500px" : "100px" }} /> - -
{(this._numTotalResults > 0 || !this._searchbarOpen) ? (null) : (
diff --git a/src/client/views/search/SearchItem.scss b/src/client/views/search/SearchItem.scss index 62715c5eb..9f12994c3 100644 --- a/src/client/views/search/SearchItem.scss +++ b/src/client/views/search/SearchItem.scss @@ -2,19 +2,27 @@ .search-overview { display: flex; - flex-direction: row-reverse; + flex-direction: reverse; justify-content: flex-end; z-index: 0; } +.link-count { + background: black; + border-radius: 20px; + color: white; + width: 15px; + text-align: center; + margin-top: 5px; +} .searchBox-placeholder, .search-overview .search-item { - width: 500px; + width: 100%; background: $light-color-secondary; border-color: $intermediate-color; border-bottom-style: solid; padding: 10px; - min-height: 70px; + min-height: 50px; max-height: 150px; height: auto; z-index: 0; @@ -61,16 +69,6 @@ overflow: hidden; position: relative; - .link-count { - opacity: 1; - position: absolute; - z-index: 1000; - text-align: center; - -webkit-transition: opacity 0.2s ease-in-out; - -moz-transition: opacity 0.2s ease-in-out; - -o-transition: opacity 0.2s ease-in-out; - transition: opacity 0.2s ease-in-out; - } .link-extended { // display: none; @@ -112,7 +110,7 @@ .icon-icons, .icon-live { - height: 50px; + height: auto; margin: auto; overflow: hidden; @@ -174,9 +172,7 @@ .searchBox-instances:active { opacity: 1; background: $lighter-alt-accent; - -webkit-transform: scale(1); - -ms-transform: scale(1); - transform: scale(1); + width:150px } .search-item:hover { @@ -193,13 +189,10 @@ .searchBox-instances { float: left; opacity: 1; - width: 150px; + width: 0px; transition: all 0.2s ease; color: black; - transform-origin: top right; - -webkit-transform: scale(0); - -ms-transform: scale(0); - transform: scale(0); + overflow: hidden; } @@ -208,7 +201,7 @@ } .searchBox-placeholder { - min-height: 70px; + min-height: 50px; margin-left: 150px; text-transform: uppercase; text-align: left; diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index a7822ed46..b8cff16f2 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -213,15 +213,6 @@ export class SearchItem extends React.Component { @computed get linkCount() { return LinkManager.Instance.getAllRelatedLinks(this.props.doc).length; } - @computed - get linkString(): string { - let num = this.linkCount; - if (num === 1) { - return num.toString() + " link"; - } - return num.toString() + " links"; - } - @action pointerDown = (e: React.PointerEvent) => { e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e); } @@ -290,7 +281,11 @@ export class SearchItem extends React.Component {
-
+
+
+
{this.linkCount}
+
+
{StrCast(this.props.doc.title)}
{this.props.highlighting.length ? "Matched fields:" + this.props.highlighting.join(", ") : this.props.lines.length ? this.props.lines[0] : ""}
@@ -301,10 +296,6 @@ export class SearchItem extends React.Component {
{this.DocumentIcon()}
{this.props.doc.type ? this.props.doc.type : "Other"}
-
-
{this.linkCount}
-
{this.linkString}
-
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index f3d5555ed..32da29932 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -97,6 +97,12 @@ export class CurrentUserUtils { curPresentation.boxShadow = "0 0"; doc.curPresentation = curPresentation; } + if (doc.searchBox === undefined) { + const searchBox = Docs.Create.QueryDocument({ title: "Searching" }); + searchBox.boxShadow = "0 0"; + searchBox.ignoreClick = true; + doc.searchBox = searchBox; + } if (doc.sidebar === undefined) { const sidebar = Docs.Create.TreeDocument([doc.workspaces as Doc, doc, doc.recentlyClosed as Doc], { title: "Sidebar" }); -- cgit v1.2.3-70-g09d2 From 3afceef2f8c2be1bddf67cecd97086a41ec6dc48 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 11 Oct 2019 16:47:42 -0400 Subject: changes to menu layouts --- src/client/documents/Documents.ts | 2 + src/client/views/GlobalKeyHandler.ts | 30 +++++- src/client/views/Main.tsx | 5 - src/client/views/MainView.tsx | 108 +++++++++++++-------- .../collections/CollectionMasonryViewFieldRow.tsx | 2 +- .../CollectionStackingViewFieldColumn.tsx | 4 +- src/client/views/nodes/ButtonBox.scss | 2 + src/client/views/nodes/DragBox.tsx | 4 +- src/client/views/nodes/IconBox.tsx | 4 +- src/client/views/search/IconBar.tsx | 2 +- src/client/views/search/IconButton.scss | 2 +- .../authentication/models/current_user_utils.ts | 73 +++++++++++--- 12 files changed, 167 insertions(+), 71 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7f6ab50d8..6b56fb443 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -74,6 +74,7 @@ export interface DocumentOptions { dropAction?: dropActionType; backgroundLayout?: string; chromeStatus?: string; + columnWidth?: number; fontSize?: number; curPage?: number; currentTimecode?: number; @@ -83,6 +84,7 @@ export interface DocumentOptions { dockingConfig?: string; autoHeight?: boolean; dbDoc?: Doc; + icon?: string; // [key: string]: Opt; } diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 557b3e366..9d239d0bf 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -9,6 +9,7 @@ import { DictationManager } from "../util/DictationManager"; import SharingManager from "../util/SharingManager"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import { Cast, PromiseValue } from "../../new_fields/Types"; +import { ScriptField } from "../../new_fields/ScriptField"; const modifiers = ["control", "meta", "shift", "alt"]; type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise; @@ -162,12 +163,31 @@ export default class KeyManager { } } break; + case "c": + if (MainView.Instance.flyoutWidth > 0) { + MainView.Instance.flyoutWidth = 0; + PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); + } else { + MainView.Instance.flyoutWidth = 400; + PromiseValue(Cast(CurrentUserUtils.UserDocument.Create, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); + } + break; + case "l": + if (MainView.Instance.flyoutWidth > 0) { + MainView.Instance.flyoutWidth = 0; + } else { + MainView.Instance.flyoutWidth = 400; + PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); + } + break; case "f": - stopPropagation = false; - preventDefault = false; - MainView.Instance.isSearchVisible = !MainView.Instance.isSearchVisible; - MainView.Instance.flyoutWidth = MainView.Instance.isSearchVisible ? 400 : 0; - PromiseValue(Cast(CurrentUserUtils.UserDocument.searchBox, Doc)).then(pv => pv && (pv.treeViewOpen = (MainView.Instance.flyoutWidth > 0))); + if (MainView.Instance.flyoutWidth > 0) { + MainView.Instance.flyoutWidth = 0; + PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); + } else { + MainView.Instance.flyoutWidth = 400; + PromiseValue(Cast(CurrentUserUtils.UserDocument.Search, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); + } break; case "o": let target = SelectionManager.SelectedDocuments()[0]; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 3bd898ac0..a91a2b69e 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -38,11 +38,6 @@ let swapDocs = async () => { if (info.id !== "__guest__") { // a guest will not have an id registered await CurrentUserUtils.loadUserDocument(info); - // updates old user documents to prevent chrome on tree view. - (await Cast(CurrentUserUtils.UserDocument.workspaces, Doc))!.chromeStatus = "disabled"; - (await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc))!.chromeStatus = "disabled"; - (await Cast(CurrentUserUtils.UserDocument.sidebar, Doc))!.chromeStatus = "disabled"; - CurrentUserUtils.UserDocument.chromeStatus = "disabled"; await swapDocs(); } document.getElementById('root')!.addEventListener('wheel', event => { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 1ec21f638..6f60de1c4 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -15,7 +15,7 @@ import { listSpec } from '../../new_fields/Schema'; import { BoolCast, Cast, FieldValue, StrCast, PromiseValue } from '../../new_fields/Types'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { RouteStore } from '../../server/RouteStore'; -import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils } from '../../Utils'; +import { emptyFunction, returnEmptyString, returnOne, returnTrue, returnFalse, Utils } from '../../Utils'; import { DocServer } from '../DocServer'; import { Docs, DocumentOptions } from '../documents/Documents'; import { ClientUtils } from '../util/ClientUtils'; @@ -40,6 +40,8 @@ import { PreviewCursor } from './PreviewCursor'; import { FilterBox } from './search/FilterBox'; import { OverlayView } from './OverlayView'; import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager'; +import { CollectionStackingView } from './collections/CollectionStackingView'; +import { ScriptField } from '../../new_fields/ScriptField'; @observer export class MainView extends React.Component { @@ -459,33 +461,63 @@ export class MainView extends React.Component { @computed get flyout() { let sidebar: FieldResult; - if (!this.userDoc || !((sidebar = this.userDoc.sidebar) instanceof Doc)) { + if (!this.userDoc || !((sidebar = this.userDoc.sidebarContainer) instanceof Doc)) { return (null); } - return - ; + (Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc).columnWidth = this.flyoutWidthFunc() / 3 - 30; + return
+
+ + +
+
+ + +
; } @computed @@ -493,7 +525,7 @@ export class MainView extends React.Component { if (!this.userDoc) { return (
{this.dockingContent}
); } - let sidebar = this.userDoc.sidebar; + let sidebar = this.userDoc.sidebarContainer; if (!(sidebar instanceof Doc)) { return (null); } @@ -585,16 +617,16 @@ export class MainView extends React.Component { // let googlePhotosSearch = () => GooglePhotosClientUtils.CollectionFromSearch(Docs.Create.MasonryDocument, { included: [GooglePhotosClientUtils.ContentCategories.LANDSCAPES] }); let btns: [React.RefObject, IconName, string, () => Doc | Promise][] = [ - [React.createRef(), "object-group", "Add Collection", addColNode], - [React.createRef(), "tv", "Add Presentation Trail", addPresNode], - [React.createRef(), "globe-asia", "Add Website", addWebNode], - [React.createRef(), "bolt", "Add Button", addButtonDocument], - [React.createRef(), "file", "Add Document Dragger", addDragboxNode], + //[React.createRef(), "object-group", "Add Collection", addColNode], + //[React.createRef(), "tv", "Add Presentation Trail", addPresNode], + //[React.createRef(), "globe-asia", "Add Website", addWebNode], + //[React.createRef(), "bolt", "Add Button", addButtonDocument], + //[React.createRef(), "file", "Add Document Dragger", addDragboxNode], // [React.createRef(), "object-group", "Test Google Photos Search", googlePhotosSearch], - [React.createRef(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], //remove at some point in favor of addImportCollectionNode + //[React.createRef(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], //remove at some point in favor of addImportCollectionNode //[React.createRef(), "play", "Add Youtube Searcher", addYoutubeSearcher], ]; - if (!ClientUtils.RELEASE) btns.unshift([React.createRef(), "cat", "Add Cat Image", addImageNode]); + //if (!ClientUtils.RELEASE) btns.unshift([React.createRef(), "cat", "Add Cat Image", addImageNode]); return < div id="add-nodes-menu" style={{ left: (this.flyoutTranslate ? this.flyoutWidth : 0) + 20, bottom: 20 }} > @@ -603,7 +635,7 @@ export class MainView extends React.Component {
    -
  • + {/*
  • */}
  • {btns.map(btn => @@ -612,7 +644,7 @@ export class MainView extends React.Component {
)} -
  • + {/*
  • */}
  • */} - + diff --git a/src/client/views/nodes/ColorBox.scss b/src/client/views/nodes/ColorBox.scss new file mode 100644 index 000000000..8df617fca --- /dev/null +++ b/src/client/views/nodes/ColorBox.scss @@ -0,0 +1,10 @@ +.colorBox-container { + width:100%; + height:100%; + position: relative; + pointer-events:all; + + .sketch-picker { + margin:auto; + } +} \ No newline at end of file diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx new file mode 100644 index 000000000..4aff770f9 --- /dev/null +++ b/src/client/views/nodes/ColorBox.tsx @@ -0,0 +1,16 @@ +import React = require("react"); +import { observer } from "mobx-react"; +import { SketchPicker } from 'react-color'; +import { FieldView, FieldViewProps } from './FieldView'; +import "./ColorBox.scss"; +import { InkingControl } from "../InkingControl"; + +@observer +export class ColorBox extends React.Component { + public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(ColorBox, fieldKey); } + render() { + return
    + +
    ; + } +} \ No newline at end of file diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index d4e7c6d4e..f2a581c42 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -25,6 +25,7 @@ import { KeyValueBox } from "./KeyValueBox"; import { PDFBox } from "./PDFBox"; import { PresBox } from "./PresBox"; import { QueryBox } from "./QueryBox"; +import { ColorBox } from "./ColorBox"; import { PresElementBox } from "../presentationview/PresElementBox"; import { VideoBox } from "./VideoBox"; import { WebBox } from "./WebBox"; @@ -100,7 +101,11 @@ export class DocumentContentsView extends React.Component(), { width: 200, height: 500, title: "a presentation trail" })'); let createFolderImport = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Import Folder", icon: "cloud-upload-alt" }); createFolderImport.onDragStart = ScriptField.MakeFunction('Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })'); - const creators = Docs.Create.MasonryDocument([createCollection, createWebPage, createCatImage, createButton, createPresentation, createFolderImport], { width: 500, height: 50, columnWidth: 35, chromeStatus: "disabled", title: "buttons" }); + const dragCreators = Docs.Create.MasonryDocument([createCollection, createWebPage, createCatImage, createButton, createPresentation, createFolderImport], { width: 500, autoHeight: true, columnWidth: 35, ignoreClick: true, chromeStatus: "disabled", title: "buttons" }); + const color = Docs.Create.ColorDocument({ title: "color picker", width: 400, ignoreClick: true }); + const creators = Docs.Create.StackingDocument([dragCreators, color], { width: 500, height: 800, chromeStatus: "disabled", title: "buttons" }) Create.targetContainer = doc.sidebarContainer; Create.creators = creators; Create.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.creators"); -- cgit v1.2.3-70-g09d2 From 988822bdeeed82b0c1ef611f2f7a3111d029d564 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 12 Oct 2019 19:37:06 -0400 Subject: a bunch of fixes to clean up drag box stuff. --- src/client/documents/DocumentTypes.ts | 2 +- src/client/documents/Documents.ts | 14 +- src/client/util/DragManager.ts | 19 +- src/client/views/GlobalKeyHandler.ts | 1 - src/client/views/Main.scss | 17 +- src/client/views/MainView.tsx | 250 ++++++++------------- src/client/views/nodes/ButtonBox.tsx | 3 +- src/client/views/nodes/DocumentContentsView.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 27 ++- src/client/views/nodes/DragBox.scss | 13 -- src/client/views/nodes/DragBox.tsx | 115 ---------- src/client/views/nodes/FontIconBox.scss | 13 ++ src/client/views/nodes/FontIconBox.tsx | 21 ++ .../authentication/models/current_user_utils.ts | 29 ++- 14 files changed, 194 insertions(+), 334 deletions(-) delete mode 100644 src/client/views/nodes/DragBox.scss delete mode 100644 src/client/views/nodes/DragBox.tsx create mode 100644 src/client/views/nodes/FontIconBox.scss create mode 100644 src/client/views/nodes/FontIconBox.tsx (limited to 'src/client/documents') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index dad2de0b5..432e53825 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -17,7 +17,7 @@ export enum DocumentType { TEMPLATE = "template", EXTENSION = "extension", YOUTUBE = "youtube", - DRAGBOX = "dragbox", + FONTICONBOX = "fonticonbox", PRES = "presentation", LINKFOLLOW = "linkfollow", PRESELEMENT = "preselement", diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e783848ff..1f89d2993 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -37,7 +37,7 @@ import { DocumentManager } from "../util/DocumentManager"; import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox"; import { Scripting, CompileScript } from "../util/Scripting"; import { ButtonBox } from "../views/nodes/ButtonBox"; -import { DragBox } from "../views/nodes/DragBox"; +import { FontIconBox } from "../views/nodes/FontIconBox"; import { SchemaHeaderField, RandomPastel } from "../../new_fields/SchemaHeaderField"; import { PresBox } from "../views/nodes/PresBox"; import { ComputedField } from "../../new_fields/ScriptField"; @@ -71,6 +71,7 @@ export interface DocumentOptions { viewType?: number; backgroundColor?: string; ignoreClick?: boolean; + lockedPosition?: boolean; opacity?: number; defaultBackgroundColor?: string; dropAction?: dropActionType; @@ -82,6 +83,7 @@ export interface DocumentOptions { currentTimecode?: number; documentText?: string; borderRounding?: string; + boxShadow?: string; schemaColumns?: List; dockingConfig?: string; autoHeight?: boolean; @@ -127,7 +129,7 @@ export namespace Docs { }], [DocumentType.QUERY, { layout: { view: QueryBox }, - options: { width: 400, fitWidth: true } + options: { width: 400 } }], [DocumentType.COLOR, { layout: { view: ColorBox }, @@ -183,8 +185,8 @@ export namespace Docs { layout: { view: PresBox }, options: {} }], - [DocumentType.DRAGBOX, { - layout: { view: DragBox }, + [DocumentType.FONTICONBOX, { + layout: { view: FontIconBox }, options: { width: 40, height: 40 }, }], [DocumentType.LINKFOLLOW, { @@ -476,8 +478,8 @@ export namespace Docs { } - export function DragboxDocument(options?: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.DRAGBOX), undefined, { ...(options || {}) }); + export function FontIconDocument(options?: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocumentType.FONTICONBOX), undefined, { ...(options || {}) }); } export function LinkFollowBoxDocument(options?: DocumentOptions) { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 7259f66e3..2c316ccdf 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,6 +1,6 @@ import { action, runInAction } from "mobx"; import { Doc, Field } from "../../new_fields/Doc"; -import { Cast, StrCast } from "../../new_fields/Types"; +import { Cast, StrCast, ScriptCast } from "../../new_fields/Types"; import { URLField } from "../../new_fields/URLField"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; @@ -13,6 +13,7 @@ import { Docs } from "../documents/Documents"; import { ScriptField } from "../../new_fields/ScriptField"; import { List } from "../../new_fields/List"; import { PrefetchProxy } from "../../new_fields/Proxy"; +import { listSpec } from "../../new_fields/Schema"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag( @@ -207,7 +208,6 @@ export namespace DragManager { } draggedDocuments: Doc[]; droppedDocuments: Doc[]; - removeDropProperties: string[] = []; offset: number[]; dropAction: dropActionType; userDropAction: dropActionType; @@ -234,17 +234,18 @@ export namespace DragManager { export let StartDragFunctions: (() => void)[] = []; - export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { + export async function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { runInAction(() => StartDragFunctions.map(func => func())); + await dragData.draggedDocuments.map(d => d.dragFactory); StartDrag(eles, dragData, downX, downY, options, options && options.finishDrag ? options.finishDrag : (dropData: { [id: string]: any }) => { - (dropData.droppedDocuments = dragData.userDropAction === "alias" || (!dragData.userDropAction && dragData.dropAction === "alias") ? - dragData.draggedDocuments.map(d => Doc.MakeAlias(d)) : - dragData.userDropAction === "copy" || (!dragData.userDropAction && dragData.dropAction === "copy") ? - dragData.draggedDocuments.map(d => Doc.MakeCopy(d, true)) : - dragData.draggedDocuments + (dropData.droppedDocuments = + dragData.draggedDocuments.map(d => ScriptCast(d.onDragStart) ? ScriptCast(d.onDragStart).script.run({ this: d }).result : + dragData.userDropAction === "alias" || (!dragData.userDropAction && dragData.dropAction === "alias") ? Doc.MakeAlias(d) : + dragData.userDropAction === "copy" || (!dragData.userDropAction && dragData.dropAction === "copy") ? Doc.MakeCopy(d, true) : d) ); - dragData.removeDropProperties.map(prop => dropData.droppedDocuments.map((d: Doc) => d[prop] = undefined)); + dropData.droppedDocuments.forEach((drop: Doc, i: number) => + Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []).map(prop => drop[prop] = undefined)); }); } diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index f3e1933d7..38fce4cf7 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -73,7 +73,6 @@ export default class KeyManager { SelectionManager.DeselectAll(); } } - main.toggleColorPicker(true); SelectionManager.DeselectAll(); DictationManager.Controls.stop(); SharingManager.Instance.close(); diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index debb941c8..55195b616 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -185,7 +185,22 @@ button:hover { overflow: auto; z-index: 1; } - +.mainContent { + width:100%; + height:100%; + position:absolute; +} +.mainView-flyoutContainer{ + display:flex; + flex-direction: column; + position: absolute; + width:100%; + height:100%; + border: black 1px solid; + .documentView-node-topmost { + background: lightgrey; + } +} #mainContent-div { width: 100%; height: 100%; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 18e5052b7..aac4af9f6 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,4 +1,4 @@ -import { IconName, library } from '@fortawesome/fontawesome-svg-core'; +import { library } from '@fortawesome/fontawesome-svg-core'; import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faChevronRight, faClone, faCloudUploadAlt, faCommentAlt, faCut, faEllipsisV, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; @@ -6,60 +6,74 @@ import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; import Measure from 'react-measure'; -import { Doc, DocListCast, Field, FieldResult, HeightSym, Opt } from '../../new_fields/Doc'; +import { Doc, DocListCast, Field, FieldResult, Opt } from '../../new_fields/Doc'; import { Id } from '../../new_fields/FieldSymbols'; import { InkTool } from '../../new_fields/InkField'; import { List } from '../../new_fields/List'; +import { ObjectField } from '../../new_fields/ObjectField'; import { listSpec } from '../../new_fields/Schema'; import { ScriptField } from '../../new_fields/ScriptField'; -import { BoolCast, Cast, FieldValue, PromiseValue, StrCast, NumCast } from '../../new_fields/Types'; +import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, StrCast } from '../../new_fields/Types'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { RouteStore } from '../../server/RouteStore'; -import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils, returnFalse } from '../../Utils'; +import { emptyFunction, returnEmptyString, returnFalse, returnOne, returnTrue, Utils } from '../../Utils'; import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; import { Docs, DocumentOptions } from '../documents/Documents'; import { DictationManager } from '../util/DictationManager'; -import { SetupDrag, DragManager } from '../util/DragManager'; +import { DragManager } from '../util/DragManager'; import { HistoryUtil } from '../util/History'; import SharingManager from '../util/SharingManager'; import { Transform } from '../util/Transform'; import { UndoManager } from '../util/UndoManager'; import { CollectionBaseView, CollectionViewType } from './collections/CollectionBaseView'; import { CollectionDockingView } from './collections/CollectionDockingView'; -import { CollectionTreeView } from './collections/CollectionTreeView'; import { ContextMenu } from './ContextMenu'; import { DocumentDecorations } from './DocumentDecorations'; import KeyManager from './GlobalKeyHandler'; import { InkingControl } from './InkingControl'; import "./Main.scss"; import MainViewModal from './MainViewModal'; +import { MainViewNotifs } from './MainViewNotifs'; import { DocumentView } from './nodes/DocumentView'; import { OverlayView } from './OverlayView'; import PDFMenu from './pdf/PDFMenu'; import { PreviewCursor } from './PreviewCursor'; -import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView'; -import { MainViewNotifs } from './MainViewNotifs'; -import { DocumentType } from '../documents/DocumentTypes'; @observer export class MainView extends React.Component { public static Instance: MainView; - @observable addMenuToggle = React.createRef(); - @observable public pwidth: number = 0; - @observable public pheight: number = 0; - private _buttonBarHeight = 85; - private dropDisposer?: DragManager.DragDropDisposer; - - @observable private dictationState = DictationManager.placeholder; - @observable private dictationSuccessState: boolean | undefined = undefined; - @observable private dictationDisplayState = false; - @observable private dictationListeningState: DictationManager.Controls.ListeningUIStatus = false; + private _buttonBarHeight = 75; + private _flyoutSizeOnDown = 0; + private _urlState: HistoryUtil.DocUrl; + private _dropDisposer?: DragManager.DragDropDisposer; + + @observable private _dictationState = DictationManager.placeholder; + @observable private _dictationSuccessState: boolean | undefined = undefined; + @observable private _dictationDisplayState = false; + @observable private _dictationListeningState: DictationManager.Controls.ListeningUIStatus = false; + + @observable private _panelWidth: number = 0; + @observable private _panelHeight: number = 0; + @observable private _flyoutTranslate: boolean = true; + @observable public addMenuToggle = React.createRef(); + @observable public flyoutWidth: number = 250; public hasActiveModal = false; - + public isPointerDown = false; public overlayTimeout: NodeJS.Timeout | undefined; + private set mainContainer(doc: Opt) { + if (doc) { + !("presentationView" in doc) && (doc.presentationView = new List([Docs.Create.TreeDocument([], { title: "Presentation" })])); + this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc); + } + } + + @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; } + @computed private get userDoc() { return CurrentUserUtils.UserDocument; } + @computed public get mainFreeform(): Opt { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } + public initiateDictationFade = () => { let duration = DictationManager.Commands.dictationFadeDuration; this.overlayTimeout = setTimeout(() => { @@ -69,13 +83,6 @@ export class MainView extends React.Component { setTimeout(() => this.dictatedPhrase = DictationManager.placeholder, 500); }, duration); } - - private urlState: HistoryUtil.DocUrl; - - @computed private get userDoc() { - return CurrentUserUtils.UserDocument; - } - public cancelDictationFade = () => { if (this.overlayTimeout) { clearTimeout(this.overlayTimeout); @@ -83,54 +90,15 @@ export class MainView extends React.Component { } } - @computed private get mainContainer(): Opt { - return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; - } - @computed get mainFreeform(): Opt { - let docs = DocListCast(this.mainContainer!.data); - return (docs && docs.length > 1) ? docs[1] : undefined; - } - public isPointerDown = false; - private set mainContainer(doc: Opt) { - if (doc) { - if (!("presentationView" in doc)) { - doc.presentationView = new List([Docs.Create.TreeDocument([], { title: "Presentation" })]); - } - this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc); - } - } - - @computed public get dictatedPhrase() { - return this.dictationState; - } - - public set dictatedPhrase(value: string) { - runInAction(() => this.dictationState = value); - } - - @computed public get dictationSuccess() { - return this.dictationSuccessState; - } - - public set dictationSuccess(value: boolean | undefined) { - runInAction(() => this.dictationSuccessState = value); - } - - @computed public get dictationOverlayVisible() { - return this.dictationDisplayState; - } + @computed public get dictatedPhrase() { return this._dictationState; } + @computed public get dictationSuccess() { return this._dictationSuccessState; } + @computed public get dictationOverlayVisible() { return this._dictationDisplayState; } + @computed public get isListening() { return this._dictationListeningState; } - public set dictationOverlayVisible(value: boolean) { - runInAction(() => this.dictationDisplayState = value); - } - - @computed public get isListening() { - return this.dictationListeningState; - } - - public set isListening(value: DictationManager.Controls.ListeningUIStatus) { - runInAction(() => this.dictationListeningState = value); - } + public set dictatedPhrase(value: string) { runInAction(() => this._dictationState = value); } + public set dictationSuccess(value: boolean | undefined) { runInAction(() => this._dictationSuccessState = value); } + public set dictationOverlayVisible(value: boolean) { runInAction(() => this._dictationDisplayState = value); } + public set isListening(value: DictationManager.Controls.ListeningUIStatus) { runInAction(() => this._dictationListeningState = value); } componentWillMount() { var tag = document.createElement('script'); @@ -147,13 +115,13 @@ export class MainView extends React.Component { //close presentation window.removeEventListener("pointerdown", this.globalPointerDown); window.removeEventListener("pointerup", this.globalPointerUp); - this.dropDisposer && this.dropDisposer(); + this._dropDisposer && this._dropDisposer(); } constructor(props: Readonly<{}>) { super(props); MainView.Instance = this; - this.urlState = HistoryUtil.parseUrl(window.location) || {} as any; + this._urlState = HistoryUtil.parseUrl(window.location) || {} as any; // causes errors to be generated when modifying an observable outside of an action configure({ enforceActions: "observed" }); if (window.location.pathname !== RouteStore.home) { @@ -233,7 +201,7 @@ export class MainView extends React.Component { { fireImmediately: true } ); } else { - if (received && this.urlState.sharing) { + if (received && this._urlState.sharing) { reaction( () => { let docking = CollectionDockingView.Instance; @@ -264,8 +232,8 @@ export class MainView extends React.Component { let freeformOptions: DocumentOptions = { x: 0, y: 400, - width: this.pwidth * .7, - height: this.pheight, + width: this._panelWidth * .7, + height: this._panelHeight, title: "My Blank Collection" }; let workspaces: FieldResult; @@ -293,7 +261,7 @@ export class MainView extends React.Component { openWorkspace = async (doc: Doc, fromHistory = false) => { CurrentUserUtils.MainDocId = doc[Id]; this.mainContainer = doc; - let state = this.urlState; + let state = this._urlState; if (state.sharing === true && !this.userDoc) { DocServer.Control.makeReadOnly(); } else { @@ -318,25 +286,21 @@ export class MainView extends React.Component { DocServer.Control.makeEditable(); } } - let col: Opt; // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized) setTimeout(async () => { - if (this.userDoc && (col = await Cast(this.userDoc.optionalRightCollection, Doc))) { - const l = Cast(col.data, listSpec(Doc)); - if (l) { - runInAction(() => MainViewNotifs.NotifsCol = col); - } - } + const col = this.userDoc && await Cast(this.userDoc.optionalRightCollection, Doc); + col && Cast(col.data, listSpec(Doc)) && runInAction(() => MainViewNotifs.NotifsCol = col); }, 100); return true; } drop = action((e: Event, de: DragManager.DropEvent) => { (de.data as DragManager.DocumentDragData).draggedDocuments.map(doc => { - if (doc.type !== DocumentType.DRAGBOX) { - let dbox = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: "bolt" }); - dbox.factory = doc; - dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.factory, true)'); + if (!doc.onDragStart) { + let dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: "bolt" }); + dbox.dragFactory = doc; + dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined; + dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); doc = dbox; } Doc.AddDocToList(CurrentUserUtils.UserDocument, "docButtons", doc); @@ -351,30 +315,22 @@ export class MainView extends React.Component { @action onResize = (r: any) => { - this.pwidth = r.offset.width; - this.pheight = r.offset.height; - } - getPWidth = () => { - return this.pwidth; - } - getPHeight = () => { - return this.pheight; - } - getContentsHeight = () => { - return this.pheight - this._buttonBarHeight; + this._panelWidth = r.offset.width; + this._panelHeight = r.offset.height; } + getPWidth = () => this._panelWidth; + getPHeight = () => this._panelHeight; + getContentsHeight = () => this._panelHeight - this._buttonBarHeight; - @observable flyoutWidth: number = 250; - @observable flyoutTranslate: boolean = true; @computed get dockingContent() { - let flyoutWidth = this.flyoutWidth; - let countWidth = this.flyoutTranslate; - let mainCont = this.mainContainer; return {({ measureRef }) => -
    - {!mainCont ? (null) : - + {!this.mainContainer ? (null) : + ; } - _downsize = 0; onPointerDown = (e: React.PointerEvent) => { - this._downsize = e.clientX; + this._flyoutSizeOnDown = e.clientX; document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); @@ -417,15 +372,15 @@ export class MainView extends React.Component { pointerOverDragger = () => { if (this.flyoutWidth === 0) { this.flyoutWidth = 250; - this.flyoutTranslate = false; + this._flyoutTranslate = false; } } @action pointerLeaveDragger = () => { - if (!this.flyoutTranslate) { + if (!this._flyoutTranslate) { this.flyoutWidth = 0; - this.flyoutTranslate = true; + this._flyoutTranslate = true; } } @@ -435,7 +390,7 @@ export class MainView extends React.Component { } @action onPointerUp = (e: PointerEvent) => { - if (Math.abs(e.clientX - this._downsize) < 4) { + if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4) { if (this.flyoutWidth < 5) this.flyoutWidth = 250; else this.flyoutWidth = 0; } @@ -461,8 +416,8 @@ export class MainView extends React.Component { return (null); } let libraryButtonDoc = Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc; - libraryButtonDoc.columnWidth = this.flyoutWidthFunc() / 3 - 30; - return
    + libraryButtonDoc.columnWidth = this.flyoutWidth / 3 - 30; + return
    + const sidebar = this.userDoc && this.userDoc.sidebarContainer; + return !this.userDoc || !(sidebar instanceof Doc) ? (null) : ( +
    {this.flyout} {this.expandButton} @@ -556,10 +508,10 @@ export class MainView extends React.Component { } @computed get expandButton() { - return !this.flyoutTranslate ? (
    { + return !this._flyoutTranslate ? (
    { runInAction(() => { this.flyoutWidth = 250; - this.flyoutTranslate = true; + this._flyoutTranslate = true; }); }}>
    ) : (null); } @@ -572,14 +524,6 @@ export class MainView extends React.Component { return { fontSize: "50%" }; } - onColorClick = (e: React.MouseEvent) => { - let target = (e.nativeEvent as any).path[0]; - let parent = (e.nativeEvent as any).path[1]; - if (target.localName === "input" || parent.localName === "span") { - e.stopPropagation(); - } - } - setWriteMode = (mode: DocServer.WriteMode) => { console.log(DocServer.WriteMode[mode]); const mode1 = mode; @@ -596,13 +540,12 @@ export class MainView extends React.Component { } protected createDropTarget = (ele: HTMLLabelElement) => { //used for stacking and masonry view - this.dropDisposer && this.dropDisposer(); + this._dropDisposer && this._dropDisposer(); if (ele) { - this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); + this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); } } - @observable private _colorPickerDisplay = false; /* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */ nodesMenu() { // let youtubeurl = "https://www.youtube.com/embed/TqcApsGRzWw"; @@ -610,14 +553,13 @@ export class MainView extends React.Component { // let googlePhotosSearch = () => GooglePhotosClientUtils.CollectionFromSearch(Docs.Create.MasonryDocument, { included: [GooglePhotosClientUtils.ContentCategories.LANDSCAPES] }); - return < div id="add-nodes-menu" style={{ left: (this.flyoutTranslate ? this.flyoutWidth : 0) + 20, bottom: 20 }} > + return < div id="add-nodes-menu" style={{ left: (this._flyoutTranslate ? this.flyoutWidth : 0) + 20, bottom: 20 }} > - +
    - {/*
  • */} @@ -658,18 +600,6 @@ export class MainView extends React.Component {
    ; } - - - @action - toggleColorPicker = (close = false) => { - this._colorPickerDisplay = close ? false : !this._colorPickerDisplay; - } - - @action.bound - toggleSearch = () => { - PromiseValue(Cast(CurrentUserUtils.UserDocument.Search, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); - } - @computed private get dictationOverlay() { let success = this.dictationSuccess; let result = this.isListening && !this.isListening.interim ? DictationManager.placeholder : `"${this.dictatedPhrase}"`; diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx index f08ea4891..3cf8c3eb3 100644 --- a/src/client/views/nodes/ButtonBox.tsx +++ b/src/client/views/nodes/ButtonBox.tsx @@ -70,7 +70,8 @@ export class ButtonBox extends DocComponent(Butt let missingParams = params && params.filter(p => this.props.Document[p] === undefined); params && params.map(p => DocListCast(this.props.Document[p])); // bcz: really hacky form of prefetching ... return ( -
    +
    {(this.Document.text || this.Document.title)} diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index f2a581c42..19ffdf0cd 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -16,7 +16,7 @@ import { AudioBox } from "./AudioBox"; import { ButtonBox } from "./ButtonBox"; import { DocumentViewProps } from "./DocumentView"; import "./DocumentView.scss"; -import { DragBox } from "./DragBox"; +import { FontIconBox } from "./FontIconBox"; import { FieldView, FieldViewProps } from "./FieldView"; import { FormattedTextBox } from "./FormattedTextBox"; import { IconBox } from "./IconBox"; @@ -102,7 +102,7 @@ export class DocumentContentsView extends React.Component(Docu if (this._mainCont.current) { let dragData = new DragManager.DocumentDragData([this.props.Document]); const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0); - dragData.offset = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top); + dragData.offset = this.Document.onDragStart ? [0, 0] : this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top); dragData.dropAction = dropAction; - dragData.moveDocument = this.props.moveDocument; + dragData.moveDocument = this.Document.onDragStart ? undefined : this.props.moveDocument; dragData.applyAsTemplate = applyAsTemplate; - dragData.removeDropProperties = this.Document.removeDropProperties || []; DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { handlers: { dragComplete: action((emptyFunction)) }, - hideSource: !dropAction + hideSource: !dropAction && !this.Document.onDragStart }); } } @@ -246,7 +247,7 @@ export class DocumentView extends DocComponent(Docu this._hitTemplateDrag = true; } } - if (this.active && e.button === 0 && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); // events stop at the lowest document that is active. if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag); + if ((this.active || this.Document.onDragStart) && e.button === 0 && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); // events stop at the lowest document that is active. if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag); document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); @@ -258,9 +259,9 @@ export class DocumentView extends DocComponent(Docu if (e.cancelBubble && this.active) { document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView) } - else if (!e.cancelBubble && (SelectionManager.IsSelected(this) || this.props.parentActive()) && !this.Document.lockedPosition && !this.Document.inOverlay) { + else if (!e.cancelBubble && (SelectionManager.IsSelected(this) || this.props.parentActive() || this.Document.onDragStart) && !this.Document.lockedPosition && !this.Document.inOverlay) { if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { - if (!e.altKey && !this.topMost && e.buttons === 1) { + if (!e.altKey && (!this.topMost || this.Document.onDragStart) && e.buttons === 1) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag); @@ -475,6 +476,12 @@ export class DocumentView extends DocComponent(Docu }); !existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" }); + let funcs: ContextMenuProps[] = []; + funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.Document.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) }); + funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.Document.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) }); + funcs.push({ description: "Drag Document", icon: "edit", event: () => this.Document.onDragStart = undefined }); + ContextMenu.Instance.addItem({ description: "OnDrag...", subitems: funcs, icon: "asterisk" }); + let existing = ContextMenu.Instance.findByDescription("Layout..."); let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : []; layoutItems.push({ description: this.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.Document.lockedPosition ? "unlock" : "lock" }); @@ -627,8 +634,8 @@ export class DocumentView extends DocComponent(Docu this.props.backgroundColor(this.Document) || StrCast(this.layoutDoc.backgroundColor) : ruleColor && !colorSet ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.Document); - const nativeWidth = this.props.Document.fitWidth ? this.props.PanelWidth() : this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%"; - const nativeHeight = this.props.Document.fitWidth ? this.props.PanelHeight() : this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; + const nativeWidth = this.props.Document.fitWidth ? this.props.PanelWidth() - 2 : this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%"; + const nativeHeight = this.props.Document.fitWidth ? this.props.PanelHeight() - 2 : this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined; const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : this.getLayoutPropStr("showTitle"); const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.getLayoutPropStr("showCaption"); diff --git a/src/client/views/nodes/DragBox.scss b/src/client/views/nodes/DragBox.scss deleted file mode 100644 index fbb9b9c1c..000000000 --- a/src/client/views/nodes/DragBox.scss +++ /dev/null @@ -1,13 +0,0 @@ -.dragBox-outerDiv { - width: 100%; - height: 100%; - pointer-events: all; - border-radius: inherit; - background: black; - border-radius: 100%; - svg { - margin:18%; - width:65% !important; - height:65%; - } -} diff --git a/src/client/views/nodes/DragBox.tsx b/src/client/views/nodes/DragBox.tsx deleted file mode 100644 index 1fd6cb5a5..000000000 --- a/src/client/views/nodes/DragBox.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { library } from '@fortawesome/fontawesome-svg-core'; -import { faEdit } from '@fortawesome/free-regular-svg-icons'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { Doc } from '../../../new_fields/Doc'; -import { createSchema, makeInterface, listSpec } from '../../../new_fields/Schema'; -import { ScriptField } from '../../../new_fields/ScriptField'; -import { emptyFunction } from '../../../Utils'; -import { CompileScript } from '../../util/Scripting'; -import { ContextMenu } from '../ContextMenu'; -import { DocComponent } from '../DocComponent'; -import { OverlayView } from '../OverlayView'; -import { ScriptBox } from '../ScriptBox'; -import { DocumentIconContainer } from './DocumentIcon'; -import './DragBox.scss'; -import { FieldView, FieldViewProps } from './FieldView'; -import { DragManager } from '../../util/DragManager'; -import { Docs } from '../../documents/Documents'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Cast } from '../../../new_fields/Types'; -import { ContextMenuProps } from '../ContextMenuItem'; - -library.add(faEdit as any); - -const DragSchema = createSchema({ - onDragStart: ScriptField, - text: "string" -}); - -type DragDocument = makeInterface<[typeof DragSchema]>; -const DragDocument = makeInterface(DragSchema); -@observer -export class DragBox extends DocComponent(DragDocument) { - _downX: number = 0; - _downY: number = 0; - public static LayoutString() { return FieldView.LayoutString(DragBox); } - _mainCont = React.createRef(); - onDragStart = (e: React.PointerEvent) => { - if (!e.ctrlKey && !e.altKey && !e.shiftKey && !this.props.isSelected() && e.button === 0) { - document.removeEventListener("pointermove", this.onDragMove); - document.addEventListener("pointermove", this.onDragMove); - document.removeEventListener("pointerup", this.onDragUp); - document.addEventListener("pointerup", this.onDragUp); - e.stopPropagation(); - e.preventDefault(); - } - } - - onDragMove = async (e: MouseEvent) => { - if (!e.cancelBubble && (Math.abs(this._downX - e.clientX) > 5 || Math.abs(this._downY - e.clientY) > 5)) { - document.removeEventListener("pointermove", this.onDragMove); - document.removeEventListener("pointerup", this.onDragUp); - const onDragStart = this.Document.onDragStart; - e.stopPropagation(); - e.preventDefault(); - let dragData = new DragManager.DocumentDragData([this.props.Document]); - const factory = await Cast(this.props.Document.factory, Doc);// if there's a factory Doc that is being copied, make sure it's not pending. - dragData.removeDropProperties = factory ? Cast(factory.removeDropProperties, listSpec("string"), []) : []; - DragManager.StartDocumentDrag([this._mainCont.current!], dragData, e.clientX, e.clientY, { - finishDrag: async (dropData) => { - let res = onDragStart && onDragStart.script.run({ this: this.props.Document }).result; - let doc = (res as Doc) || Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" }); - dropData.droppedDocuments = [doc]; - dragData.removeDropProperties.map(prop => dropData.droppedDocuments.map((d: Doc) => d[prop] = undefined)); - }, - handlers: { dragComplete: emptyFunction }, - hideSource: false - }); - } - e.stopPropagation(); - e.preventDefault(); - } - - onDragUp = (e: MouseEvent) => { - document.removeEventListener("pointermove", this.onDragMove); - document.removeEventListener("pointerup", this.onDragUp); - } - - onContextMenu = () => { - let funcs: ContextMenuProps[] = []; - funcs.push({ description: "Set Make Aliases", icon: "edit", event: () => this.props.Document.factory && (this.props.Document.onDragStart = ScriptField.MakeFunction('getAlias(this.factory)')) }); - funcs.push({ description: "Set Make Copies", icon: "edit", event: () => this.props.Document.factory && (this.props.Document.onDragStart = ScriptField.MakeFunction('getCopy(this.factory, true)')) }); - funcs.push({ - description: "Edit OnClick script", icon: "edit", event: () => { - let overlayDisposer: () => void = emptyFunction; - const script = this.Document.onDragStart; - let originalText: string | undefined = undefined; - if (script) originalText = script.script.originalScript; - // tslint:disable-next-line: no-unnecessary-callback-wrapper - let scriptingBox = overlayDisposer()} onSave={(text, onError) => { - const script = CompileScript(text, { - params: { this: Doc.name }, - typecheck: false, - editable: true, - transformer: DocumentIconContainer.getTransformer() - }); - if (!script.compiled) { - onError(script.errors.map(error => error.messageText).join("\n")); - return; - } - this.Document.onClick = new ScriptField(script); - overlayDisposer(); - }} showDocumentIcons />; - overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${this.Document.title || ""} OnDragStart` }); - } - }); - ContextMenu.Instance.addItem({ description: "DragBox Funcs...", subitems: funcs, icon: "asterisk" }); - } - - render() { - return (
    - -
    ); - } -} \ No newline at end of file diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss new file mode 100644 index 000000000..75d093fcb --- /dev/null +++ b/src/client/views/nodes/FontIconBox.scss @@ -0,0 +1,13 @@ +.fontIconBox-outerDiv { + width: 100%; + height: 100%; + pointer-events: all; + border-radius: inherit; + background: black; + border-radius: 100%; + svg { + margin:18%; + width:65% !important; + height:65%; + } +} diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx new file mode 100644 index 000000000..3b580d851 --- /dev/null +++ b/src/client/views/nodes/FontIconBox.tsx @@ -0,0 +1,21 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { createSchema, makeInterface } from '../../../new_fields/Schema'; +import { DocComponent } from '../DocComponent'; +import './FontIconBox.scss'; +import { FieldView, FieldViewProps } from './FieldView'; +const FontIconSchema = createSchema({ + icon: "string" +}); + +type FontIconDocument = makeInterface<[typeof FontIconSchema]>; +const FontIconDocument = makeInterface(FontIconSchema); +@observer +export class FontIconBox extends DocComponent(FontIconDocument) { + public static LayoutString() { return FieldView.LayoutString(FontIconBox); } + + render() { + return
    ; + } +} \ No newline at end of file diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 74a41d8cf..9f9f79dc4 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -99,9 +99,9 @@ export class CurrentUserUtils { } if (doc.Library === undefined) { - let Search = Docs.Create.ButtonDocument({ width: 50, height: 35, title: "Search" }); - let Library = Docs.Create.ButtonDocument({ width: 50, height: 35, title: "Library" }); - let Create = Docs.Create.ButtonDocument({ width: 35, height: 35, title: "Create" }); + let Search = Docs.Create.ButtonDocument({ width: 50, height: 35, borderRounding: "50%", boxShadow: "2px 2px 1px", title: "Search" }); + let Library = Docs.Create.ButtonDocument({ width: 50, height: 35, borderRounding: "50%", boxShadow: "2px 2px 1px", title: "Library" }); + let Create = Docs.Create.ButtonDocument({ width: 35, height: 35, borderRounding: "50%", boxShadow: "2px 2px 1px", title: "Create" }); if (doc.sidebarContainer === undefined) { doc.sidebarContainer = new Doc(); (doc.sidebarContainer as Doc).chromeStatus = "disabled"; @@ -114,40 +114,39 @@ export class CurrentUserUtils { library.xMargin = 5; library.yMargin = 5; library.dropAction = "alias"; - library.boxShadow = "1 1 3"; Library.targetContainer = doc.sidebarContainer; Library.library = library; Library.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.library"); - const searchBox = Docs.Create.QueryDocument({ title: "Searching" }); - searchBox.boxShadow = "0 0"; + const searchBox = Docs.Create.QueryDocument({ title: "search stack" }); searchBox.ignoreClick = true; Search.searchBox = searchBox; Search.targetContainer = doc.sidebarContainer; Search.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.searchBox"); - let createCollection = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Collection", icon: "folder" }); - let createWebPage = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Web Page", icon: "globe-asia" }); + let createCollection = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Collection", icon: "folder" }); + createCollection.onDragStart = ScriptField.MakeFunction('Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" })'); + let createWebPage = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Web Page", icon: "globe-asia" }); createWebPage.onDragStart = ScriptField.MakeFunction('Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" })'); - let createCatImage = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Image", icon: "cat" }); + let createCatImage = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Image", icon: "cat" }); createCatImage.onDragStart = ScriptField.MakeFunction('Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { width: 200, title: "an image of a cat" })'); - let createButton = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Button", icon: "bolt" }); + let createButton = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Button", icon: "bolt" }); createButton.onDragStart = ScriptField.MakeFunction('Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })'); - let createPresentation = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Presentation", icon: "tv" }); + let createPresentation = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Presentation", icon: "tv" }); createPresentation.onDragStart = ScriptField.MakeFunction('Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List(), { width: 200, height: 500, title: "a presentation trail" })'); - let createFolderImport = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Import Folder", icon: "cloud-upload-alt" }); + let createFolderImport = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Import Folder", icon: "cloud-upload-alt" }); createFolderImport.onDragStart = ScriptField.MakeFunction('Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })'); - const dragCreators = Docs.Create.MasonryDocument([createCollection, createWebPage, createCatImage, createButton, createPresentation, createFolderImport], { width: 500, autoHeight: true, columnWidth: 35, ignoreClick: true, chromeStatus: "disabled", title: "buttons" }); + const dragCreators = Docs.Create.MasonryDocument([createCollection, createWebPage, createCatImage, createButton, createPresentation, createFolderImport], { width: 500, autoHeight: true, columnWidth: 35, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons" }); const color = Docs.Create.ColorDocument({ title: "color picker", width: 400 }); color.dropAction = "alias"; color.ignoreClick = true; color.removeDropProperties = new List(["dropAction", "ignoreClick"]); - const creators = Docs.Create.StackingDocument([dragCreators, color], { width: 500, height: 800, chromeStatus: "disabled", title: "buttons" }); + const creators = Docs.Create.StackingDocument([dragCreators, color], { width: 500, height: 800, chromeStatus: "disabled", title: "creator stack" }); Create.targetContainer = doc.sidebarContainer; Create.creators = creators; Create.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.creators"); - const libraryButtons = Docs.Create.StackingDocument([Search, Library, Create], { width: 500, height: 80, chromeStatus: "disabled", title: "buttons" }); + const libraryButtons = Docs.Create.StackingDocument([Search, Library, Create], { width: 500, height: 80, chromeStatus: "disabled", title: "library stack" }); libraryButtons.sectionFilter = "title"; libraryButtons.boxShadow = "0 0"; libraryButtons.ignoreClick = true; -- cgit v1.2.3-70-g09d2 From 26e215b0cddbb4c14cfd8eb7a720a373e797c615 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 15 Oct 2019 00:23:00 -0400 Subject: cleaned up current_user_utils. cleaned up some interactions with colorBox, textbox height --- src/client/documents/Documents.ts | 19 +- src/client/views/DocComponent.tsx | 34 ++- src/client/views/InkingControl.tsx | 8 + src/client/views/MainView.tsx | 16 +- src/client/views/nodes/ColorBox.scss | 7 +- src/client/views/nodes/ColorBox.tsx | 12 +- src/client/views/nodes/FormattedTextBox.tsx | 13 +- src/client/views/nodes/PDFBox.tsx | 3 +- .../authentication/models/current_user_utils.ts | 295 ++++++++++----------- 9 files changed, 229 insertions(+), 178 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 1f89d2993..13cc7815c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -40,7 +40,7 @@ import { ButtonBox } from "../views/nodes/ButtonBox"; import { FontIconBox } from "../views/nodes/FontIconBox"; import { SchemaHeaderField, RandomPastel } from "../../new_fields/SchemaHeaderField"; import { PresBox } from "../views/nodes/PresBox"; -import { ComputedField } from "../../new_fields/ScriptField"; +import { ComputedField, ScriptField } from "../../new_fields/ScriptField"; import { ProxyField } from "../../new_fields/Proxy"; import { DocumentType } from "./DocumentTypes"; import { LinkFollowBox } from "../views/linking/LinkFollowBox"; @@ -65,7 +65,10 @@ export interface DocumentOptions { page?: number; scale?: number; fitWidth?: boolean; + forceActive?: boolean; + preventTreeViewOpen?: boolean; // ignores the treeViewOpen Doc flag which allows a treeViewItem's expande/collapse state to be independent of other views of the same document in the tree view layout?: string | Doc; + hideHeadings?: boolean; // whether stacking view column headings should be hidden isTemplate?: boolean; templates?: List; viewType?: number; @@ -84,11 +87,21 @@ export interface DocumentOptions { documentText?: string; borderRounding?: string; boxShadow?: string; + sectionFilter?: string; // field key used to determine headings for sections in stacking and masonry views schemaColumns?: List; dockingConfig?: string; autoHeight?: boolean; + removeDropProperties?: List; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document dbDoc?: Doc; + onClick?: ScriptField; + onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop icon?: string; + gridGap?: number; // gap between items in masonry view + xMargin?: number; // gap between left edge of document and start of masonry/stacking layouts + yMargin?: number; // gap between top edge of dcoument and start of masonry/stacking layouts + panel?: Doc; // panel to display in 'targetContainer' as the result of a button onClick script + targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script + convertToButtons?: boolean; // whether documents dropped onto a collection should be converted to buttons that will construct the dropped document // [key: string]: Opt; } @@ -457,6 +470,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Freeform }, id); } + export function LinearDocument(documents: Array, options: DocumentOptions, id?: string) { + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Linear }, id); + } + export function SchemaDocument(schemaColumns: SchemaHeaderField[], documents: Array, options: DocumentOptions) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema }); } diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 2c5992259..b05966bb5 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -1,10 +1,17 @@ import * as React from 'react'; import { Doc } from '../../new_fields/Doc'; import { computed, action } from 'mobx'; -import { Cast } from '../../new_fields/Types'; +import { Cast, BoolCast } from '../../new_fields/Types'; import { listSpec } from '../../new_fields/Schema'; +import { InkingControl } from './InkingControl'; +import { InkTool } from '../../new_fields/InkField'; -export function DocComponent

    (schemaCtor: (doc: Doc) => T) { + +/// DocComponents returns a generic base class for React views of document fields that are not interactive +interface DocComponentProps { + Document: Doc; +} +export function DocComponent

    (schemaCtor: (doc: Doc) => T) { class Component extends React.Component

    { //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then @computed @@ -15,6 +22,27 @@ export function DocComponent

    (schemaCtor: (doc: D return Component; } + +/// DocStaticProps return a base class for React views of document fields that are interactive only when selected (e.g. ColorBox) +interface DocStaticProps { + Document: Doc; + isSelected: () => boolean; + renderDepth: number; +} +export function DocStaticComponent

    (schemaCtor: (doc: Doc) => T) { + class Component extends React.Component

    { + //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then + @computed + get Document(): T { + return schemaCtor(this.props.Document); + } + active = () => (this.props.Document.forceActive || this.props.isSelected() || this.props.renderDepth === 0) && !InkingControl.Instance.selectedTool; + } + return Component; +} + + +/// DocAnnotatbleComponent return a base class for React views of document fields that are annotatable *and* interactive when selected (e.g., pdf, image) interface DocAnnotatableProps { Document: Doc; DataDoc?: Doc; @@ -57,7 +85,7 @@ export function DocAnnotatableComponent

    (schema return Doc.AddDocToList(this.extensionDoc, this.props.fieldExt, doc); } whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive); - active = () => this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; + active = () => (InkingControl.Instance.selectedTool === InkTool.None) && (BoolCast(this.props.Document.forceActive) || this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0); } return Component; } \ No newline at end of file diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 38734a03d..d42d8d2d9 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -48,6 +48,14 @@ export class InkingControl extends React.Component { let selected = SelectionManager.SelectedDocuments(); let oldColors = selected.map(view => { let targetDoc = view.props.Document.layout instanceof Doc ? view.props.Document.layout : view.props.Document.isTemplate ? view.props.Document : Doc.GetProto(view.props.Document); + let sel = window.getSelection(); + if (StrCast(targetDoc.layout).indexOf("FormattedTextBox") !== -1 && (!sel || sel.toString() !== "")) { + targetDoc.color = this._selectedColor; + return { + target: targetDoc, + previous: StrCast(targetDoc.color) + }; + } let oldColor = StrCast(targetDoc.backgroundColor); let matchedColor = this._selectedColor; const cvd = view.props.ContainingCollectionDoc; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index ddea3e223..701f094e2 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -343,12 +343,12 @@ export class MainView extends React.Component { if (!(sidebarContent instanceof Doc)) { return (null); } - let libraryButtonDoc = Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc; - libraryButtonDoc.columnWidth = this.flyoutWidth / 3 - 30; + let sidebarButtonsDoc = Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc; + sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30; return

    { - Doc.AddDocToList(CurrentUserUtils.UserDocument, "docButtons", doc); + Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc); return true; } remButtonDoc = (doc: Doc) => { - Doc.RemoveDocFromList(CurrentUserUtils.UserDocument, "docButtons", doc); + Doc.RemoveDocFromList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc); return true; } @computed get docButtons() { return
    - ; +const ColorDocument = makeInterface(documentSchema); @observer -export class ColorBox extends React.Component { +export class ColorBox extends DocStaticComponent(ColorDocument) { public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(ColorBox, fieldKey); } render() { - return
    + return
    e.button === 0 && !e.ctrlKey && e.stopPropagation()}>
    ; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index b05d0046c..181f37d36 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -43,7 +43,6 @@ import { FormattedTextBoxComment, formattedTextBoxCommentPlugin } from './Format import React = require("react"); import { ContextMenuProps } from '../ContextMenuItem'; import { ContextMenu } from '../ContextMenu'; -import { TextShadowProperty } from 'csstype'; library.add(faEdit); library.add(faSmile, faTextHeight, faUpload); @@ -490,7 +489,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe ); this._heightReactionDisposer = reaction( - () => this.props.Document[WidthSym](), + () => [this.props.Document[WidthSym](), this.props.Document.autoHeight], () => this.tryUpdateHeight() ); @@ -984,13 +983,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe @action tryUpdateHeight() { - const ChromeHeight = this.props.ChromeHeight; - let sh = this._ref.current ? this._ref.current.scrollHeight : 0; - if (!this.props.Document.isAnimating && this.props.Document.autoHeight && sh !== 0 && getComputedStyle(this._ref.current!.parentElement!).top === "0px") { + let scrollHeight = this._ref.current ? this._ref.current.scrollHeight : 0; + if (!this.props.Document.isAnimating && this.props.Document.autoHeight && scrollHeight !== 0 && + getComputedStyle(this._ref.current!.parentElement!).top === "0px") { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation let nh = this.props.Document.isTemplate ? 0 : NumCast(this.dataDoc.nativeHeight, 0); let dh = NumCast(this.props.Document.height, 0); - this.props.Document.height = Math.max(10, (nh ? dh / nh * sh : sh) + (ChromeHeight ? ChromeHeight() : 0)); - this.dataDoc.nativeHeight = nh ? sh : undefined; + this.props.Document.height = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0)); + this.dataDoc.nativeHeight = nh ? scrollHeight : undefined; } } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 63b412a23..19797400f 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -115,7 +115,6 @@ export class PDFBox extends DocAnnotatableComponent private newScriptChange = (e: React.ChangeEvent) => this._scriptValue = e.currentTarget.value; whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive); - active = () => this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; setPdfViewer = (pdfViewer: PDFViewer) => { this._pdfViewer = pdfViewer; }; searchStringChanged = (e: React.ChangeEvent) => this._searchString = e.currentTarget.value; @@ -205,7 +204,7 @@ export class PDFBox extends DocAnnotatableComponent _initialScale: number | undefined; // the initial scale of the PDF when first rendered which determines whether the document will be live on startup or not. Getting bigger after startup won't make it automatically be live.... render() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); - let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.active ? "" : "-interactive"); + let classname = "pdfBox-cont" + (this.active() ? "-interactive" : ""); let noPdf = !(pdfUrl instanceof PdfField) || !this._pdf; if (this._initialScale === undefined) this._initialScale = this.props.ScreenToLocalTransform().Scale; if (this.props.isSelected() || this.props.Document.scrollY !== undefined) this._everActive = true; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 73cac879e..8af6bbfd5 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -1,20 +1,17 @@ -import { action, computed, observable, runInAction, reaction } from "mobx"; +import { action, computed, observable, reaction, runInAction } from "mobx"; import * as rp from 'request-promise'; import { DocServer } from "../../../client/DocServer"; import { Docs } from "../../../client/documents/Documents"; import { Attribute, AttributeGroup, Catalog, Schema } from "../../../client/northstar/model/idea/idea"; import { ArrayUtil } from "../../../client/northstar/utils/ArrayUtil"; -import { CollectionViewType } from "../../../client/views/collections/CollectionBaseView"; -import { CollectionView } from "../../../client/views/collections/CollectionView"; +import { UndoManager } from "../../../client/util/UndoManager"; import { Doc, DocListCast } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; -import { Cast, StrCast, PromiseValue } from "../../../new_fields/Types"; +import { ScriptField } from "../../../new_fields/ScriptField"; +import { Cast, PromiseValue } from "../../../new_fields/Types"; import { Utils } from "../../../Utils"; import { RouteStore } from "../../RouteStore"; -import { ScriptField } from "../../../new_fields/ScriptField"; -import { ButtonBox } from "../../../client/views/nodes/ButtonBox"; -import { UndoManager } from "../../../client/util/UndoManager"; export class CurrentUserUtils { private static curr_id: string; @@ -22,176 +19,172 @@ export class CurrentUserUtils { private static mainDocId: string | undefined; public static get id() { return this.curr_id; } - @computed public static get UserDocument() { return Doc.UserDoc(); } public static get MainDocId() { return this.mainDocId; } public static set MainDocId(id: string | undefined) { this.mainDocId = id; } + @computed public static get UserDocument() { return Doc.UserDoc(); } @observable public static GuestTarget: Doc | undefined; @observable public static GuestWorkspace: Doc | undefined; private static createUserDocument(id: string): Doc { let doc = new Doc(id, true); - doc.viewType = CollectionViewType.Tree; - doc.layout = CollectionView.LayoutString(); doc.title = Doc.CurrentUserEmail; - doc.data = new List(); - doc.gridGap = 5; - doc.xMargin = 5; - doc.yMargin = 5; - doc.height = 42; - doc.boxShadow = "0 0"; - doc.convertToButtons = true; // for CollectionLinearView used as the docButton layout - doc.optionalRightCollection = Docs.Create.StackingDocument([], { title: "New mobile uploads" }); return this.updateUserDocument(doc);// this should be the last } - static updateUserDocument(doc: Doc) { + // a default set of note types .. not being used yet... + static setupNoteTypes(doc: Doc) { + let notes = [ + Docs.Create.TextDocument({ title: "Note", backgroundColor: "yellow", isTemplate: true }), + Docs.Create.TextDocument({ title: "Idea", backgroundColor: "pink", isTemplate: true }), + Docs.Create.TextDocument({ title: "Topic", backgroundColor: "lightBlue", isTemplate: true }), + Docs.Create.TextDocument({ title: "Person", backgroundColor: "lightGreen", isTemplate: true }) + ]; + doc.noteTypes = Docs.Create.TreeDocument(notes, { title: "Note Types", height: 75 }); + } - if (doc.undoBtn === undefined) { - doc.undoBtn = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Collection", icon: "undo-alt" }); - (doc.undoBtn as Doc).onClick = ScriptField.MakeScript('undo()'); - Doc.AddDocToList(doc, "docButtons", doc.undoBtn as Doc); - } - if (doc.redoBtn === undefined) { - doc.redoBtn = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Collection", icon: "redo-alt" }); - (doc.redoBtn as Doc).onClick = ScriptField.MakeScript('redo()'); - Doc.AddDocToList(doc, "docButtons", doc.redoBtn as Doc); - } + // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools + static setupCreatorButtons() { + let docProtoData = [ + { title: "collection", icon: "folder", script: 'Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" })' }, + { title: "web page", icon: "globe-asia", script: 'Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" })' }, + { title: "image", icon: "cat", script: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { width: 200, title: "an image of a cat" })' }, + { title: "button", icon: "bolt", script: 'Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })' }, + { title: "presentation", icon: "tv", script: 'Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List(), { width: 200, height: 500, title: "a presentation trail" })' }, + { title: "import folder", icon: "cloud-upload-alt", script: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })' }, + ]; + return docProtoData.map(data => Docs.Create.FontIconDocument({ + nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, + title: data.title, icon: data.icon, onDragStart: ScriptField.MakeFunction(data.script) + })); + } + + // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker. when clicked, this panel will be displayed in the target container (ie, sidebarContainer) + static setupCreatePanel(sidebarContainer: Doc) { + // setup a masonry view of all he creators + const dragCreators = Docs.Create.MasonryDocument(CurrentUserUtils.setupCreatorButtons(), { + width: 500, autoHeight: true, columnWidth: 35, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons" + }); + // setup a color picker + const color = Docs.Create.ColorDocument({ + title: "color picker", width: 400, removeDropProperties: new List(["dropAction", "forceActive"]) + }); + color.dropAction = "alias"; // these must be set on the view document so they can't be part of the creator above. + color.forceActive = true; + + return Docs.Create.ButtonDocument({ + width: 35, height: 35, borderRounding: "50%", boxShadow: "2px 2px 1px", title: "Create", targetContainer: sidebarContainer, + panel: Docs.Create.StackingDocument([dragCreators, color], { + width: 500, height: 800, chromeStatus: "disabled", title: "creator stack" + }), + onClick: ScriptField.MakeScript("this.targetContainer.proto = this.panel") + }); + } + + // setup the Library button which will display the library panel. This panel includes a collection of workspaces, documents, and recently closed views + static setupLibraryPanel(sidebarContainer: Doc, doc: Doc) { // setup workspaces library item - if (doc.workspaces === undefined) { - const workspaces = Docs.Create.TreeDocument([], { title: "WORKSPACES", height: 100 }); - workspaces.boxShadow = "0 0"; - doc.workspaces = workspaces; - } - PromiseValue(Cast(doc.workspaces, Doc)).then(workspaces => { - if (workspaces) { - workspaces.backgroundColor = "#eeeeee"; - workspaces.preventTreeViewOpen = true; - workspaces.forceActive = true; - workspaces.lockedPosition = true; - if (StrCast(workspaces.title) === "Workspaces") { - workspaces.title = "WORKSPACES"; - } - } + doc.workspaces = Docs.Create.TreeDocument([], { + title: "WORKSPACES", height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, backgroundColor: "#eeeeee" }); - // setup notes list - if (doc.noteTypes === undefined) { - let notes = [Docs.Create.TextDocument({ title: "Note", backgroundColor: "yellow", isTemplate: true }), - Docs.Create.TextDocument({ title: "Idea", backgroundColor: "pink", isTemplate: true }), - Docs.Create.TextDocument({ title: "Topic", backgroundColor: "lightBlue", isTemplate: true }), - Docs.Create.TextDocument({ title: "Person", backgroundColor: "lightGreen", isTemplate: true })]; - const noteTypes = Docs.Create.TreeDocument(notes, { title: "Note Types", height: 75 }); - doc.noteTypes = noteTypes; - } - PromiseValue(Cast(doc.noteTypes, Doc)).then(noteTypes => noteTypes && PromiseValue(noteTypes.data).then(DocListCast)); + doc.documents = Docs.Create.TreeDocument([], { + title: "DOCUMENTS", gridGap: 5, xMargin: 5, yMargin: 5, height: 42, width: 100, boxShadow: "0 0", backgroundColor: "#eeeeee", preventTreeViewOpen: true, forceActive: true, lockedPosition: true + }); // setup Recently Closed library item - if (doc.recentlyClosed === undefined) { - const recentlyClosed = Docs.Create.TreeDocument([], { title: "Recently Closed".toUpperCase(), height: 75 }); - recentlyClosed.boxShadow = "0 0"; - doc.recentlyClosed = recentlyClosed; - } - PromiseValue(Cast(doc.recentlyClosed, Doc)).then(recent => { - if (recent) { - recent.backgroundColor = "#eeeeee"; - recent.preventTreeViewOpen = true; - recent.forceActive = true; - recent.lockedPosition = true; - if (StrCast(recent.title) === "Recently Closed") { - recent.title = "RECENTLY CLOSED"; - } - } + doc.recentlyClosed = Docs.Create.TreeDocument([], { + title: "Recently Closed".toUpperCase(), height: 75, boxShadow: "0 0", preventTreeViewOpen: true, forceActive: true, lockedPosition: true, backgroundColor: "#eeeeee" }); + return Docs.Create.ButtonDocument({ + width: 50, height: 35, borderRounding: "50%", boxShadow: "2px 2px 1px", title: "Library", + panel: Docs.Create.TreeDocument([doc.workspaces as Doc, doc, doc.recentlyClosed as Doc], { + title: "Library", xMargin: 5, yMargin: 5, gridGap: 5, forceActive: true, dropAction: "alias", lockedPosition: true + }), + targetContainer: sidebarContainer, + onClick: ScriptField.MakeScript("this.targetContainer.proto = this.panel") + }); + } - if (doc.curPresentation === undefined) { - const curPresentation = Docs.Create.PresDocument(new List(), { title: "Presentation" }); - curPresentation.boxShadow = "0 0"; - doc.curPresentation = curPresentation; - } + // setup the Search button which will display the search panel. + static setupSearchPanel(sidebarContainer: Doc) { + return Docs.Create.ButtonDocument({ + width: 50, height: 35, borderRounding: "50%", boxShadow: "2px 2px 1px", title: "Search", + panel: Docs.Create.QueryDocument({ + title: "search stack", ignoreClick: true + }), + targetContainer: sidebarContainer, + onClick: ScriptField.MakeScript("this.targetContainer.proto = this.panel") + }); + } - if (doc.Library === undefined) { - let Search = Docs.Create.ButtonDocument({ width: 50, height: 35, borderRounding: "50%", boxShadow: "2px 2px 1px", title: "Search" }); - let Library = Docs.Create.ButtonDocument({ width: 50, height: 35, borderRounding: "50%", boxShadow: "2px 2px 1px", title: "Library" }); - let Create = Docs.Create.ButtonDocument({ width: 35, height: 35, borderRounding: "50%", boxShadow: "2px 2px 1px", title: "Create" }); - if (doc.sidebarContainer === undefined) { - doc.sidebarContainer = new Doc(); - (doc.sidebarContainer as Doc).chromeStatus = "disabled"; - } + // setup the list of sidebar mode buttons which determine what is displayed in the sidebar + static setupSidebarButtons(doc: Doc) { + doc.sidebarContainer = new Doc(); + (doc.sidebarContainer as Doc).chromeStatus = "disabled"; - const library = Docs.Create.TreeDocument([doc.workspaces as Doc, doc, doc.recentlyClosed as Doc], { title: "Library" }); - library.forceActive = true; - library.lockedPosition = true; - library.gridGap = 5; - library.xMargin = 5; - library.yMargin = 5; - library.dropAction = "alias"; - Library.targetContainer = doc.sidebarContainer; - Library.library = library; - Library.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.library"); + doc.CreateBtn = this.setupCreatePanel(doc.sidebarContainer as Doc); + doc.LibraryBtn = this.setupLibraryPanel(doc.sidebarContainer as Doc, doc); + doc.SearchBtn = this.setupSearchPanel(doc.sidebarContainer as Doc); - const searchBox = Docs.Create.QueryDocument({ title: "search stack" }); - searchBox.ignoreClick = true; - Search.searchBox = searchBox; - Search.targetContainer = doc.sidebarContainer; - Search.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.searchBox"); + // Finally, setup the list of buttons to display in the sidebar + doc.sidebarButtons = Docs.Create.StackingDocument([doc.SearchBtn as Doc, doc.LibraryBtn as Doc, doc.CreateBtn as Doc], { + width: 500, height: 80, boxShadow: "0 0", sectionFilter: "title", hideHeadings: true, ignoreClick: true, + backgroundColor: "lightgrey", chromeStatus: "disabled", title: "library stack" + }); + } - let createCollection = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Collection", icon: "folder" }); - createCollection.onDragStart = ScriptField.MakeFunction('Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" })'); - let createWebPage = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Web Page", icon: "globe-asia" }); - createWebPage.onDragStart = ScriptField.MakeFunction('Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" })'); - let createCatImage = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Image", icon: "cat" }); - createCatImage.onDragStart = ScriptField.MakeFunction('Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { width: 200, title: "an image of a cat" })'); - let createButton = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Button", icon: "bolt" }); - createButton.onDragStart = ScriptField.MakeFunction('Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })'); - let createPresentation = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Presentation", icon: "tv" }); - createPresentation.onDragStart = ScriptField.MakeFunction('Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List(), { width: 200, height: 500, title: "a presentation trail" })'); - let createFolderImport = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Import Folder", icon: "cloud-upload-alt" }); - createFolderImport.onDragStart = ScriptField.MakeFunction('Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })'); - const dragCreators = Docs.Create.MasonryDocument([createCollection, createWebPage, createCatImage, createButton, createPresentation, createFolderImport], { width: 500, autoHeight: true, columnWidth: 35, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons" }); - const color = Docs.Create.ColorDocument({ title: "color picker", width: 400 }); - color.dropAction = "alias"; - color.ignoreClick = true; - color.removeDropProperties = new List(["dropAction", "ignoreClick"]); - const creators = Docs.Create.StackingDocument([dragCreators, color], { width: 500, height: 800, chromeStatus: "disabled", title: "creator stack" }); - Create.targetContainer = doc.sidebarContainer; - Create.creators = creators; - Create.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.creators"); + /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window + static setupExpandingButtons(doc: Doc) { + doc.undoBtn = Docs.Create.FontIconDocument( + { nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" }); + doc.redoBtn = Docs.Create.FontIconDocument( + { nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" }); - const libraryButtons = Docs.Create.StackingDocument([Search, Library, Create], { width: 500, height: 80, chromeStatus: "disabled", title: "library stack" }); - libraryButtons.sectionFilter = "title"; - libraryButtons.boxShadow = "0 0"; - libraryButtons.ignoreClick = true; - libraryButtons.hideHeadings = true; - libraryButtons.backgroundColor = "lightgrey"; + doc.expandingButtons = Docs.Create.LinearDocument([doc.undoBtn as Doc, doc.redoBtn as Doc], { + title: "expanding buttons", gridGap: 5, xMargin: 5, yMargin: 5, height: 42, width: 100, boxShadow: "0 0", + backgroundColor: "#eeeeee", preventTreeViewOpen: true, forceActive: true, lockedPosition: true, convertToButtons: true + }); + } - doc.libraryButtons = libraryButtons; - doc.Library = Library; - doc.Create = Create; - doc.Search = Search; - } - PromiseValue(Cast(doc.libraryButtons, Doc)).then(libraryButtons => { }); - PromiseValue(Cast(doc.Library, Doc)).then(library => library && library.library && library.targetContainer && (library.onClick as ScriptField).script.run({ this: library })); - PromiseValue(Cast(doc.Create, Doc)).then(async create => create && create.creators && create.targetContainer); - PromiseValue(Cast(doc.Search, Doc)).then(async search => search && search.searchBox && search.targetContainer); + // sets up the default set of documents to be shown in the Overlay layer + static setupOverlays(doc: Doc) { + doc.overlays = Docs.Create.FreeformDocument([], { title: "Overlays", backgroundColor: "#aca3a6" }); + doc.linkFollowBox = Docs.Create.LinkFollowBoxDocument({ x: 250, y: 20, width: 500, height: 370, title: "Link Follower" }); + Doc.AddDocToList(doc.overlays as Doc, "data", doc.linkFollowBox as Doc); + } - if (doc.overlays === undefined) { - const overlays = Docs.Create.FreeformDocument([], { title: "Overlays" }); - Doc.GetProto(overlays).backgroundColor = "#aca3a6"; - doc.overlays = overlays; - } + // the initial presentation Doc to use + static setupDefaultPresentation(doc: Doc) { + doc.curPresentation = Docs.Create.PresDocument(new List(), { title: "Presentation", boxShadow: "0 0" }); + } - if (doc.linkFollowBox === undefined) { - PromiseValue(Cast(doc.overlays, Doc)).then(overlays => overlays && Doc.AddDocToList(overlays, "data", doc.linkFollowBox = Docs.Create.LinkFollowBoxDocument({ x: 250, y: 20, width: 500, height: 370, title: "Link Follower" }))); - } + static setupMobileUploads(doc: Doc) { + doc.optionalRightCollection = Docs.Create.StackingDocument([], { title: "New mobile uploads" }); + } + + static updateUserDocument(doc: Doc) { + (doc.optionalRightCollection === undefined) && CurrentUserUtils.setupMobileUploads(doc); + (doc.noteTypes === undefined) && CurrentUserUtils.setupNoteTypes(doc); + (doc.overlays === undefined) && CurrentUserUtils.setupOverlays(doc); + (doc.expandingButtons === undefined) && CurrentUserUtils.setupExpandingButtons(doc); + (doc.curPresentation === undefined) && CurrentUserUtils.setupDefaultPresentation(doc); + (doc.sidebarButtons === undefined) && CurrentUserUtils.setupSidebarButtons(doc); + + // this is equivalent to using PrefetchProxies to make sure all the sidebarButtons and noteType internal Doc's have been retrieved. + PromiseValue(Cast(doc.noteTypes, Doc)).then(noteTypes => noteTypes && PromiseValue(noteTypes.data).then(DocListCast)); + PromiseValue(Cast(doc.sidebarButtons, Doc)).then(stackingDoc => { + stackingDoc && PromiseValue(Cast(stackingDoc.data, listSpec(Doc))).then(sidebarButtons => { + sidebarButtons && sidebarButtons.map((sidebarBtn, i) => { + sidebarBtn && PromiseValue(Cast(sidebarBtn, Doc)).then(async btn => { + btn && btn.panel && btn.targetContainer && i === 1 && (btn.onClick as ScriptField).script.run({ this: btn }); + }); + }); + }); + }); - doc.title = "DOCUMENTS"; - doc.backgroundColor = "#eeeeee"; - doc.width = 100; - doc.preventTreeViewOpen = true; - doc.forceActive = true; - doc.lockedPosition = true; + // setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet doc.undoBtn && reaction(() => UndoManager.undoStack.slice(), () => (doc.undoBtn as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); doc.redoBtn && reaction(() => UndoManager.redoStack.slice(), () => (doc.redoBtn as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true }); @@ -215,12 +208,8 @@ export class CurrentUserUtils { await rp.get(Utils.prepend(RouteStore.getUserDocumentId)).then(id => { if (id && id !== "guest") { return DocServer.GetRefField(id).then(async field => { - if (field instanceof Doc) { - await this.updateUserDocument(field); - runInAction(() => Doc.SetUserDoc(field)); - } else { - runInAction(() => Doc.SetUserDoc(this.createUserDocument(id))); - } + let userDoc = field instanceof Doc ? await this.updateUserDocument(field) : this.createUserDocument(id); + runInAction(() => Doc.SetUserDoc(userDoc)); }); } 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 69d822d6c68b69e5bfeeff03942ea9bab71cecc3 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 15 Oct 2019 00:36:35 -0400 Subject: tweak to linearView layouts with onClick items --- src/client/documents/DocumentTypes.ts | 2 +- src/client/documents/Documents.ts | 4 ++-- src/client/views/CollectionLinearView.scss | 4 ++-- src/client/views/CollectionLinearView.tsx | 3 ++- 4 files changed, 7 insertions(+), 6 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 432e53825..03178bbdb 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -17,7 +17,7 @@ export enum DocumentType { TEMPLATE = "template", EXTENSION = "extension", YOUTUBE = "youtube", - FONTICONBOX = "fonticonbox", + FONTICON = "fonticonbox", PRES = "presentation", LINKFOLLOW = "linkfollow", PRESELEMENT = "preselement", diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 13cc7815c..5794a6bee 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -198,7 +198,7 @@ export namespace Docs { layout: { view: PresBox }, options: {} }], - [DocumentType.FONTICONBOX, { + [DocumentType.FONTICON, { layout: { view: FontIconBox }, options: { width: 40, height: 40 }, }], @@ -496,7 +496,7 @@ export namespace Docs { export function FontIconDocument(options?: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.FONTICONBOX), undefined, { ...(options || {}) }); + return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { ...(options || {}) }); } export function LinkFollowBoxDocument(options?: DocumentOptions) { diff --git a/src/client/views/CollectionLinearView.scss b/src/client/views/CollectionLinearView.scss index 1e6bb5922..4bfd88b69 100644 --- a/src/client/views/CollectionLinearView.scss +++ b/src/client/views/CollectionLinearView.scss @@ -50,11 +50,11 @@ padding: 0; position: relative; - .collectionLinearView-docBtn { + .collectionLinearView-docBtn, .collectionLinearView-docBtn-scalable { position:relative; margin-right: 10px; } - .collectionLinearView-docBtn:hover { + .collectionLinearView-docBtn-scalable:hover { transform: scale(1.15); } diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx index 49c67a448..5c793f784 100644 --- a/src/client/views/CollectionLinearView.tsx +++ b/src/client/views/CollectionLinearView.tsx @@ -17,6 +17,7 @@ import { DocumentView, documentSchema } from './nodes/DocumentView'; import "./CollectionLinearView.scss"; import { makeInterface } from '../../new_fields/Schema'; import { CollectionSubView } from './collections/CollectionSubView'; +import { DocumentType } from '../documents/DocumentTypes'; type LinearDocument = makeInterface<[typeof documentSchema,]>; @@ -87,7 +88,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
    {this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => -
    +
    Date: Tue, 15 Oct 2019 13:42:13 -0400 Subject: extensions for linearViews or nesting. --- src/client/documents/Documents.ts | 2 +- src/client/views/CollectionLinearView.scss | 24 +++--- src/client/views/CollectionLinearView.tsx | 88 +++++++++++----------- src/client/views/DocComponent.tsx | 2 +- src/client/views/InkingControl.tsx | 58 +++++--------- src/client/views/MainView.tsx | 80 ++++++++++++-------- src/client/views/collections/CollectionSubView.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 15 ++-- src/client/views/nodes/FontIconBox.tsx | 5 +- .../authentication/models/current_user_utils.ts | 26 ++++--- 10 files changed, 154 insertions(+), 147 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5794a6bee..6df172f46 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -471,7 +471,7 @@ export namespace Docs { } export function LinearDocument(documents: Array, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Linear }, id); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", backgroundColor: "black", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Linear }, id); } export function SchemaDocument(schemaColumns: SchemaHeaderField[], documents: Array, options: DocumentOptions) { diff --git a/src/client/views/CollectionLinearView.scss b/src/client/views/CollectionLinearView.scss index 4bfd88b69..4423a7020 100644 --- a/src/client/views/CollectionLinearView.scss +++ b/src/client/views/CollectionLinearView.scss @@ -4,26 +4,25 @@ .collectionLinearView-outer{ overflow: hidden; height:100%; - padding:5px; .collectionLinearView { display:flex; + height: 100%; >label { background: $dark-color; color: $light-color; display: inline-block; border-radius: 18px; - font-size: 25px; - width: 36px; - height: 36px; - margin-right: 10px; + font-size: 12.5px; + width: 18px; + height: 18px; + margin-top:auto; + margin-bottom:auto; cursor: pointer; transition: transform 0.2s; } label p { - padding-left: 10.5px; - width: 500px; - height: 500px; + padding-left:5px; } label:hover { @@ -47,20 +46,21 @@ .collectionLinearView-content { display: flex; opacity: 1; - padding: 0; position: relative; + margin-top: auto; .collectionLinearView-docBtn, .collectionLinearView-docBtn-scalable { position:relative; - margin-right: 10px; + margin-top: auto; + margin-bottom: auto; } .collectionLinearView-docBtn-scalable:hover { transform: scale(1.15); } .collectionLinearView-round-button { - width: 36px; - height: 36px; + width: 18px; + height: 18px; border-radius: 18px; font-size: 15px; } diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx index 5c793f784..9d1dd7749 100644 --- a/src/client/views/CollectionLinearView.tsx +++ b/src/client/views/CollectionLinearView.tsx @@ -1,23 +1,20 @@ -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable, computed, IReactionDisposer, reaction } from 'mobx'; +import { action, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, DocListCast, Opt, HeightSym } from '../../new_fields/Doc'; -import { InkTool } from '../../new_fields/InkField'; +import { Doc, HeightSym, WidthSym } from '../../new_fields/Doc'; import { ObjectField } from '../../new_fields/ObjectField'; +import { makeInterface } from '../../new_fields/Schema'; import { ScriptField } from '../../new_fields/ScriptField'; -import { NumCast, StrCast } from '../../new_fields/Types'; -import { emptyFunction, returnEmptyString, returnOne, returnTrue, returnFalse, Utils } from '../../Utils'; +import { BoolCast, NumCast, StrCast } from '../../new_fields/Types'; +import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils } from '../../Utils'; import { Docs } from '../documents/Documents'; import { DragManager } from '../util/DragManager'; import { Transform } from '../util/Transform'; -import { UndoManager } from '../util/UndoManager'; -import { InkingControl } from './InkingControl'; -import { DocumentView, documentSchema } from './nodes/DocumentView'; import "./CollectionLinearView.scss"; -import { makeInterface } from '../../new_fields/Schema'; +import { CollectionViewType } from './collections/CollectionBaseView'; import { CollectionSubView } from './collections/CollectionSubView'; -import { DocumentType } from '../documents/DocumentTypes'; +import { documentSchema, DocumentView } from './nodes/DocumentView'; +import { translate } from 'googleapis/build/src/apis/translate'; type LinearDocument = makeInterface<[typeof documentSchema,]>; @@ -26,7 +23,6 @@ const LinearDocument = makeInterface(documentSchema); @observer export class CollectionLinearView extends CollectionSubView(LinearDocument) { @observable public addMenuToggle = React.createRef(); - @observable private _checked = false; private _dropDisposer?: DragManager.DragDropDisposer; private _heightDisposer?: IReactionDisposer; @@ -37,12 +33,8 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { componentDidMount() { // is there any reason this needs to exist? -syip. yes, it handles autoHeight for stacking views (masonry isn't yet supported). - this._heightDisposer = reaction(() => NumCast(this.props.Document.height, 0) + this.childDocs.length + (this._checked ? 1 : 0), - () => { - if (true || this.props.Document.fitWidth) { - this.props.Document.width = 36 + (this._checked ? this.childDocs.length * (this.props.Document[HeightSym]() + 10) : 10); - } - }, + this._heightDisposer = reaction(() => NumCast(this.props.Document.height, 0) + this.childDocs.length + (this.props.Document.isExpanded ? 1 : 0), + () => this.props.Document.width = 18 + (this.props.Document.isExpanded ? this.childDocs.length * (this.props.Document[HeightSym]()) : 10), { fireImmediately: true } ); } @@ -56,11 +48,13 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { drop = action((e: Event, de: DragManager.DropEvent) => { (de.data as DragManager.DocumentDragData).draggedDocuments.map((doc, i) => { let dbox = doc; - if (!doc.onDragStart && this.props.Document.convertToButtons) { + if (!doc.onDragStart && this.props.Document.convertToButtons && doc.viewType !== CollectionViewType.Linear) { dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: "bolt" }); dbox.dragFactory = doc; dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined; dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); + } else if (doc.viewType === CollectionViewType.Linear) { + dbox.ignoreClick = true; } (de.data as DragManager.DocumentDragData).droppedDocuments[i] = dbox; }); @@ -68,40 +62,51 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { return super.drop(e, de); }); - selected = (tool: InkTool) => { - if (!InkingControl.Instance || InkingControl.Instance.selectedTool === InkTool.None) return { display: "none" }; - if (InkingControl.Instance.selectedTool === tool) { - return { color: "#61aaa3", fontSize: "50%" }; - } - return { fontSize: "50%" }; - } - public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); } - dimension = () => NumCast(this.props.Document.height) - 5; + dimension = () => NumCast(this.props.Document.height); // 2 * the padding + getTransform = (ele: React.RefObject) => () => { + if (!ele.current) return Transform.Identity(); + let { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current); + return new Transform(-translateX, -translateY, 1 / scale); + }; + _spacing = 20; render() { let guid = Utils.GenerateGuid(); return
    - this._checked = this.addMenuToggle.current!.checked)} /> - + this.props.Document.isExpanded = this.addMenuToggle.current!.checked)} /> +
    - {this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => -
    + {this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => { + let nested = pair.layout.viewType === CollectionViewType.Linear; + let dref = React.createRef(); + let nativeWidth = NumCast(pair.layout.nativeWidth, this.dimension()); + let scalingContent = nested ? 1 : this.dimension() / (this._spacing + nativeWidth); + let scalingBox = nested ? 1 : this.dimension() / nativeWidth; + let deltaSize = nativeWidth * scalingBox - nativeWidth * scalingContent; + return
    this.dimension() / (10 + NumCast(pair.layout.nativeWidth, this.dimension()))} // ugh - need to get rid of this inline function to avoid recomputing - PanelWidth={this.dimension} - PanelHeight={this.dimension} + ScreenToLocalTransform={this.getTransform(dref)} + ContentScaling={() => scalingContent} // ugh - need to get rid of this inline function to avoid recomputing + PanelWidth={() => nested ? pair.layout[WidthSym]() : this.dimension()} + PanelHeight={() => nested ? pair.layout[HeightSym]() : this.dimension()} renderDepth={this.props.renderDepth + 1} focus={emptyFunction} backgroundColor={returnEmptyString} @@ -113,15 +118,10 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { zoomToScale={emptyFunction} getScale={returnOne}> -
    )} +
    + })} {/*
  • */} - {this.props.showHiddenControls ? <> - - - - - - : (null)} +
    ; diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index b05966bb5..fbc27192c 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -36,7 +36,7 @@ export function DocStaticComponent

    (schemaCtor: (doc get Document(): T { return schemaCtor(this.props.Document); } - active = () => (this.props.Document.forceActive || this.props.isSelected() || this.props.renderDepth === 0) && !InkingControl.Instance.selectedTool; + active = () => (this.props.Document.forceActive || this.props.isSelected() || this.props.renderDepth === 0);// && !InkingControl.Instance.selectedTool; // bcz: inking state shouldn't affect static tools } return Component; } diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index d42d8d2d9..41dec9f83 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -1,38 +1,30 @@ -import { observable, action, computed, runInAction } from "mobx"; +import { action, computed, observable } from "mobx"; import { ColorResult } from 'react-color'; -import React = require("react"); -import { observer } from "mobx-react"; -import "./InkingControl.scss"; -import { library } from '@fortawesome/fontawesome-svg-core'; -import { faPen, faHighlighter, faEraser, faBan } from '@fortawesome/free-solid-svg-icons'; -import { SelectionManager } from "../util/SelectionManager"; -import { InkTool } from "../../new_fields/InkField"; import { Doc } from "../../new_fields/Doc"; -import { undoBatch, UndoManager } from "../util/UndoManager"; -import { StrCast, NumCast, Cast } from "../../new_fields/Types"; -import { listSpec } from "../../new_fields/Schema"; +import { InkTool } from "../../new_fields/InkField"; import { List } from "../../new_fields/List"; +import { listSpec } from "../../new_fields/Schema"; +import { Cast, NumCast, StrCast } from "../../new_fields/Types"; import { Utils } from "../../Utils"; +import { Scripting } from "../util/Scripting"; +import { SelectionManager } from "../util/SelectionManager"; +import { undoBatch, UndoManager } from "../util/UndoManager"; -library.add(faPen, faHighlighter, faEraser, faBan); -@observer -export class InkingControl extends React.Component { +export class InkingControl { @observable static Instance: InkingControl; @observable private _selectedTool: InkTool = InkTool.None; @observable private _selectedColor: string = "rgb(244, 67, 54)"; @observable private _selectedWidth: string = "5"; @observable public _open: boolean = false; - constructor(props: Readonly<{}>) { - super(props); - runInAction(() => InkingControl.Instance = this); + constructor() { + InkingControl.Instance = this; } - @action - switchTool = (tool: InkTool): void => { + switchTool = action((tool: InkTool): void => { this._selectedTool = tool; - } + }) decimalToHexString(number: number) { if (number < 0) { number = 0xFFFFFFFF + number + 1; @@ -124,22 +116,10 @@ export class InkingControl extends React.Component { return this._selectedWidth; } - @action - toggleDisplay = () => { - this._open = !this._open; - this.switchTool(this._open ? InkTool.Pen : InkTool.None); - } - render() { - return ( -

      -
    • - - ) => this.switchWidth(e.target.value)} /> - ) => this.switchWidth(e.target.value)} /> -
    • -
    - ); - } -} \ No newline at end of file +} +Scripting.addGlobal(function activatePen() { return InkingControl.Instance.switchTool(InkTool.Pen); }); +Scripting.addGlobal(function activateBrush() { return InkingControl.Instance.switchTool(InkTool.Highlighter); }); +Scripting.addGlobal(function activateEraser() { return InkingControl.Instance.switchTool(InkTool.Eraser); }); +Scripting.addGlobal(function deactivateInk() { return InkingControl.Instance.switchTool(InkTool.None); }); +Scripting.addGlobal(function setInkWidth(width: any) { return InkingControl.Instance.switchWidth(width); }); +Scripting.addGlobal(function setInkColor(color: any) { return InkingControl.Instance.updateSelectedColor(color); }); \ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 701f094e2..4e06763a4 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,5 +1,8 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faChevronRight, faClone, faCloudUploadAlt, faCommentAlt, faCut, faEllipsisV, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt } from '@fortawesome/free-solid-svg-icons'; +import { + faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faChevronRight, faClone, faCloudUploadAlt, faCommentAlt, faCut, faEllipsisV, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, + faMusic, faObjectGroup, faPause, faPenNib, faPen, faEraser, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt +} from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -99,6 +102,8 @@ export class MainView extends React.Component { library.add(faGlobeAsia); library.add(faUndoAlt); library.add(faRedoAlt); + library.add(faPen); + library.add(faEraser); library.add(faPenNib); library.add(faFilm); library.add(faMusic); @@ -450,37 +455,50 @@ export class MainView extends React.Component { Doc.RemoveDocFromList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc); return true; } + @action + moveButtonDoc = (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean): boolean => { + return this.remButtonDoc(doc) && addDocument(doc); + } + buttonBarXf = () => { + if (!this._docBtnRef.current) return Transform.Identity(); + let { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current); + return new Transform(-translateX, -translateY, 1 / scale); + } + _docBtnRef = React.createRef(); @computed get docButtons() { - return
    - - -
    ; + console.log("stuff = " + this.flyoutWidthFunc() + " " + this.getContentsHeight()); + if (CurrentUserUtils.UserDocument.expandingButtons instanceof Doc) { + return
    + + +
    ; + } + return (null); } render() { diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 6e8e4fa12..fdbe5339d 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -33,7 +33,6 @@ export interface CollectionViewProps extends FieldViewProps { VisibleHeight?: () => number; chromeCollapsed: boolean; setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; - showHiddenControls?: boolean; // hack for showing the undo/redo/ink controls in a linear view -- needs to be redone } export interface SubCollectionViewProps extends CollectionViewProps { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 62264ea38..c0e5185c1 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -39,6 +39,7 @@ import { ImageField } from '../../../new_fields/URLField'; import SharingManager from '../../util/SharingManager'; import { Scripting } from '../../util/Scripting'; import { DictationOverlay } from '../DictationOverlay'; +import { CollectionViewType } from '../collections/CollectionBaseView'; library.add(fa.faEdit); library.add(fa.faTrash); @@ -105,7 +106,7 @@ export const documentSchema = createSchema({ dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias" or "copy") removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped onClick: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) - onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return an Doc to drag. + onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped. dragFactory: Doc, // the document that serves as the "template" for the onDragStart script ignoreAspect: "boolean", // whether aspect ratio should be ignored when laying out or manipulating the document autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents @@ -167,7 +168,7 @@ export class DocumentView extends DocComponent(Docu if (this._mainCont.current) { let dragData = new DragManager.DocumentDragData([this.props.Document]); const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0); - dragData.offset = this.Document.onDragStart ? [0, 0] : this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top); + dragData.offset = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top); dragData.dropAction = dropAction; dragData.moveDocument = this.Document.onDragStart ? undefined : this.props.moveDocument; dragData.applyAsTemplate = applyAsTemplate; @@ -175,7 +176,7 @@ export class DocumentView extends DocComponent(Docu handlers: { dragComplete: action((emptyFunction)) }, - hideSource: !dropAction && !this.Document.onDragStart + hideSource: !dropAction && !this.Document.onDragStart && !this.Document.onClick }); } } @@ -249,7 +250,7 @@ export class DocumentView extends DocComponent(Docu this._hitTemplateDrag = true; } } - if ((this.active || this.Document.onDragStart) && e.button === 0 && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); // events stop at the lowest document that is active. if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag); + if ((this.active || this.Document.onDragStart || this.Document.onClick) && e.button === 0 && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); // events stop at the lowest document that is active. if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag); document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); @@ -261,9 +262,9 @@ export class DocumentView extends DocComponent(Docu if (e.cancelBubble && this.active) { document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView) } - else if (!e.cancelBubble && (SelectionManager.IsSelected(this) || this.props.parentActive() || this.Document.onDragStart) && !this.Document.lockedPosition && !this.Document.inOverlay) { + else if (!e.cancelBubble && (SelectionManager.IsSelected(this) || this.props.parentActive() || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) { if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { - if (!e.altKey && (!this.topMost || this.Document.onDragStart) && e.buttons === 1) { + if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick) && e.buttons === 1) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag); @@ -684,7 +685,7 @@ export class DocumentView extends DocComponent(Docu color: StrCast(this.Document.color), outline: fullDegree && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px", border: fullDegree && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined, - background: backgroundColor, + background: this.props.Document.type === DocumentType.FONTICON || this.props.Document.viewType === CollectionViewType.Linear ? undefined : backgroundColor, width: animwidth, height: animheight, transform: `scale(${this.props.Document.fitWidth ? 1 : this.props.ContentScaling()})`, diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx index 3f5afb6d1..aa442cd92 100644 --- a/src/client/views/nodes/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox.tsx @@ -5,6 +5,7 @@ import { createSchema, makeInterface } from '../../../new_fields/Schema'; import { DocComponent } from '../DocComponent'; import './FontIconBox.scss'; import { FieldView, FieldViewProps } from './FieldView'; +import { StrCast } from '../../../new_fields/Types'; const FontIconSchema = createSchema({ icon: "string" }); @@ -16,6 +17,8 @@ export class FontIconBox extends DocComponent( public static LayoutString() { return FieldView.LayoutString(FontIconBox); } render() { - return ; + return ; } } \ No newline at end of file diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 8af6bbfd5..c4b91dadd 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -12,6 +12,7 @@ import { ScriptField } from "../../../new_fields/ScriptField"; import { Cast, PromiseValue } from "../../../new_fields/Types"; import { Utils } from "../../../Utils"; import { RouteStore } from "../../RouteStore"; +import { InkingControl } from "../../../client/views/InkingControl"; export class CurrentUserUtils { private static curr_id: string; @@ -45,17 +46,21 @@ export class CurrentUserUtils { // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools static setupCreatorButtons() { - let docProtoData = [ - { title: "collection", icon: "folder", script: 'Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" })' }, - { title: "web page", icon: "globe-asia", script: 'Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" })' }, - { title: "image", icon: "cat", script: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { width: 200, title: "an image of a cat" })' }, - { title: "button", icon: "bolt", script: 'Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })' }, - { title: "presentation", icon: "tv", script: 'Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List(), { width: 200, height: 500, title: "a presentation trail" })' }, - { title: "import folder", icon: "cloud-upload-alt", script: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })' }, + let docProtoData: { title: string, icon: string, drag?: string, click?: string }[] = [ + { title: "collection", icon: "folder", drag: 'Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" })' }, + { title: "web page", icon: "globe-asia", drag: 'Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" })' }, + { title: "image", icon: "cat", drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { width: 200, title: "an image of a cat" })' }, + { title: "button", icon: "bolt", drag: 'Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })' }, + { title: "presentation", icon: "tv", drag: 'Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List(), { width: 200, height: 500, title: "a presentation trail" })' }, + { title: "import folder", icon: "cloud-upload-alt", drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })' }, + { title: "pen", icon: "pen-nib", click: 'activatePen(); setInkWidth(2);' }, + { title: "highlighter", icon: "pen", click: 'activateBrush(); setInkWidth(20);' }, + { title: "eraser", icon: "eraser", click: 'activateEraser();' }, + { title: "none", icon: "pause", click: 'deactivateInk();' }, ]; return docProtoData.map(data => Docs.Create.FontIconDocument({ - nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, - title: data.title, icon: data.icon, onDragStart: ScriptField.MakeFunction(data.script) + nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.click ? "alias" : undefined, + title: data.title, icon: data.icon, onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, onClick: data.click ? ScriptField.MakeScript(data.click) : undefined })); } @@ -144,7 +149,7 @@ export class CurrentUserUtils { doc.expandingButtons = Docs.Create.LinearDocument([doc.undoBtn as Doc, doc.redoBtn as Doc], { title: "expanding buttons", gridGap: 5, xMargin: 5, yMargin: 5, height: 42, width: 100, boxShadow: "0 0", - backgroundColor: "#eeeeee", preventTreeViewOpen: true, forceActive: true, lockedPosition: true, convertToButtons: true + backgroundColor: "black", preventTreeViewOpen: true, forceActive: true, lockedPosition: true, convertToButtons: true }); } @@ -165,6 +170,7 @@ export class CurrentUserUtils { } static updateUserDocument(doc: Doc) { + new InkingControl(); (doc.optionalRightCollection === undefined) && CurrentUserUtils.setupMobileUploads(doc); (doc.noteTypes === undefined) && CurrentUserUtils.setupNoteTypes(doc); (doc.overlays === undefined) && CurrentUserUtils.setupOverlays(doc); -- cgit v1.2.3-70-g09d2 From 33811c112c7e479813908ba10f72813954a3e289 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 15 Oct 2019 16:25:10 -0400 Subject: working version of inking buttons --- src/client/documents/Documents.ts | 2 ++ src/client/util/DragManager.ts | 4 +-- src/client/util/SelectionManager.ts | 11 -------- src/client/views/CollectionLinearView.tsx | 4 +-- src/client/views/InkingControl.tsx | 6 ++-- src/client/views/MainView.tsx | 6 ++-- src/client/views/nodes/ColorBox.tsx | 33 ++++++++++++++++++++-- src/client/views/nodes/DocumentView.tsx | 7 +++-- src/client/views/nodes/FontIconBox.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 16 ++++++----- src/new_fields/Doc.ts | 4 +++ .../authentication/models/current_user_utils.ts | 31 +++++++++++--------- 12 files changed, 78 insertions(+), 48 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 6df172f46..f4fce34ac 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -93,6 +93,8 @@ export interface DocumentOptions { autoHeight?: boolean; removeDropProperties?: List; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document dbDoc?: Doc; + unchecked?: ScriptField; // returns whether a check box is unchecked + activePen?: Doc; // which pen document is currently active (used as the radio button state for the 'unhecked' pen tool scripts) onClick?: ScriptField; onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop icon?: string; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 169f2edec..92666c03c 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -234,9 +234,9 @@ export namespace DragManager { export let StartDragFunctions: (() => void)[] = []; - export async function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { + export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { runInAction(() => StartDragFunctions.map(func => func())); - await dragData.draggedDocuments.map(d => d.dragFactory); + dragData.draggedDocuments.map(d => d.dragFactory); // does this help? trying to make sure the dragFactory Doc is loaded StartDrag(eles, dragData, downX, downY, options, options && options.finishDrag ? options.finishDrag : (dropData: { [id: string]: any }) => { (dropData.droppedDocuments = diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index df1b46b33..2d717ca57 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -45,17 +45,6 @@ export namespace SelectionManager { } const manager = new Manager(); - reaction(() => manager.SelectedDocuments, sel => { - let targetColor = "#FFFFFF"; - if (sel.length > 0) { - let firstView = sel[0]; - let doc = firstView.props.Document; - let targetDoc = doc.isTemplate ? doc : Doc.GetProto(doc); - let stored = StrCast(targetDoc.backgroundColor); - stored.length > 0 && (targetColor = stored); - } - InkingControl.Instance.updateSelectedColor(targetColor); - }, { fireImmediately: true }); export function DeselectDoc(docView: DocumentView): void { manager.DeselectDoc(docView); diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx index 9d1dd7749..eb3c768d0 100644 --- a/src/client/views/CollectionLinearView.tsx +++ b/src/client/views/CollectionLinearView.tsx @@ -48,7 +48,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { drop = action((e: Event, de: DragManager.DropEvent) => { (de.data as DragManager.DocumentDragData).draggedDocuments.map((doc, i) => { let dbox = doc; - if (!doc.onDragStart && this.props.Document.convertToButtons && doc.viewType !== CollectionViewType.Linear) { + if (!doc.onDragStart && !doc.onClick && this.props.Document.convertToButtons && doc.viewType !== CollectionViewType.Linear) { dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: "bolt" }); dbox.dragFactory = doc; dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined; @@ -90,7 +90,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { return
    (); @computed get docButtons() { - console.log("stuff = " + this.flyoutWidthFunc() + " " + this.getContentsHeight()); if (CurrentUserUtils.UserDocument.expandingButtons instanceof Doc) { - return
    + return
    ; const ColorDocument = makeInterface(documentSchema); @@ -14,9 +17,35 @@ const ColorDocument = makeInterface(documentSchema); @observer export class ColorBox extends DocStaticComponent(ColorDocument) { public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(ColorBox, fieldKey); } + _selectedDisposer: IReactionDisposer | undefined; + componentDidMount() { + this._selectedDisposer = reaction(() => SelectionManager.SelectedDocuments(), + action(() => this._startupColor = SelectionManager.SelectedDocuments().length ? StrCast(SelectionManager.SelectedDocuments()[0].Document.backgroundColor, "black") : "black"), + { fireImmediately: true }); + + // compare to this reaction that used to be in Selection Manager + // reaction(() => manager.SelectedDocuments, sel => { + // let targetColor = "#FFFFFF"; + // if (sel.length > 0) { + // let firstView = sel[0]; + // let doc = firstView.props.Document; + // let targetDoc = doc.isTemplate ? doc : Doc.GetProto(doc); + // let stored = StrCast(targetDoc.backgroundColor); + // stored.length > 0 && (targetColor = stored); + // } + // InkingControl.Instance.updateSelectedColor(targetColor); + // }, { fireImmediately: true }); + } + componentWillUnmount() { + this._selectedDisposer && this._selectedDisposer(); + } + + @observable _startupColor = "black"; + render() { - return
    e.button === 0 && !e.ctrlKey && e.stopPropagation()}> - + return
    e.button === 0 && !e.ctrlKey && e.stopPropagation()}> +
    ; } } \ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index c0e5185c1..6f99c541f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -107,7 +107,7 @@ export const documentSchema = createSchema({ removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped onClick: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped. - dragFactory: Doc, // the document that serves as the "template" for the onDragStart script + dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document. ignoreAspect: "boolean", // whether aspect ratio should be ignored when laying out or manipulating the document autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents isTemplate: "boolean", // whether this document acts as a template layout for describing how other documents should be displayed @@ -676,6 +676,7 @@ export class DocumentView extends DocComponent(Docu const highlightColors = ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"]; const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid", "solid"]; + let highlighting = fullDegree && this.props.Document.type !== DocumentType.FONTICON && this.props.Document.viewType !== CollectionViewType.Linear return (
    (Docu transition: this.props.Document.isAnimating !== undefined ? ".5s linear" : StrCast(this.Document.transition), pointerEvents: this.Document.isBackground && !this.isSelected() ? "none" : "all", color: StrCast(this.Document.color), - outline: fullDegree && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px", - border: fullDegree && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined, + outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px", + border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined, background: this.props.Document.type === DocumentType.FONTICON || this.props.Document.viewType === CollectionViewType.Linear ? undefined : backgroundColor, width: animwidth, height: animheight, diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx index aa442cd92..efe47d8a8 100644 --- a/src/client/views/nodes/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox.tsx @@ -18,7 +18,7 @@ export class FontIconBox extends DocComponent( render() { return ; } } \ No newline at end of file diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 181f37d36..1bdff3ec7 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -927,16 +927,18 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe onMouseUp = (e: React.MouseEvent): void => { e.stopPropagation(); - // this interposes on prosemirror's upHandler to prevent prosemirror's up from invoked multiple times when there are nested prosemirrors. We only want the lowest level prosemirror to be invoked. - if ((this._editorView as any).mouseDown) { - let originalUpHandler = (this._editorView as any).mouseDown.up; - (this._editorView as any).root.removeEventListener("mouseup", originalUpHandler); - (this._editorView as any).mouseDown.up = (e: MouseEvent) => { + let view = this._editorView as any; + // this interposes on prosemirror's upHandler to prevent prosemirror's up from invoked multiple times when there + // are nested prosemirrors. We only want the lowest level prosemirror to be invoked. + if (view.mouseDown) { + let originalUpHandler = view.mouseDown.up; + view.root.removeEventListener("mouseup", originalUpHandler); + view.mouseDown.up = (e: MouseEvent) => { !(e as any).formattedHandled && originalUpHandler(e); - e.stopPropagation(); + // e.stopPropagation(); (e as any).formattedHandled = true; }; - (this._editorView as any).root.addEventListener("mouseup", (this._editorView as any).mouseDown.up); + view.root.addEventListener("mouseup", view.mouseDown.up); } } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 4bed113e3..276596fb8 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -525,6 +525,7 @@ export namespace Doc { export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string): Doc { const copy = new Doc(copyProtoId, true); Object.keys(doc).forEach(key => { + let cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); const field = ProxyField.WithoutProxy(() => doc[key]); if (key === "proto" && copyProto) { if (doc[key] instanceof Doc) { @@ -533,6 +534,8 @@ export namespace Doc { } else { if (field instanceof RefField) { copy[key] = field; + } else if (cfield instanceof ComputedField) { + copy[key] = ComputedField.MakeFunction(cfield.script.originalScript); } else if (field instanceof ObjectField) { copy[key] = ObjectField.MakeCopy(field); } else if (field instanceof Promise) { @@ -735,5 +738,6 @@ Scripting.addGlobal(function getCopy(doc: any, copyProto: any) { return Doc.Make Scripting.addGlobal(function copyField(field: any) { return ObjectField.MakeCopy(field); }); Scripting.addGlobal(function aliasDocs(field: any) { return new List(field.map((d: any) => Doc.MakeAlias(d))); }); Scripting.addGlobal(function docList(field: any) { return DocListCast(field); }); +Scripting.addGlobal(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2) }); Scripting.addGlobal(function undo() { return UndoManager.Undo(); }); Scripting.addGlobal(function redo() { return UndoManager.Redo(); }); \ No newline at end of file diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index c4b91dadd..3c4a46ed8 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -8,7 +8,7 @@ import { UndoManager } from "../../../client/util/UndoManager"; import { Doc, DocListCast } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; -import { ScriptField } from "../../../new_fields/ScriptField"; +import { ScriptField, ComputedField } from "../../../new_fields/ScriptField"; import { Cast, PromiseValue } from "../../../new_fields/Types"; import { Utils } from "../../../Utils"; import { RouteStore } from "../../RouteStore"; @@ -45,29 +45,32 @@ export class CurrentUserUtils { } // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools - static setupCreatorButtons() { - let docProtoData: { title: string, icon: string, drag?: string, click?: string }[] = [ + static setupCreatorButtons(doc: Doc) { + doc.activePen = doc; + let docProtoData: { title: string, icon: string, drag?: string, click?: string, unchecked?: string, activePen?: Doc, backgroundColor?: string }[] = [ { title: "collection", icon: "folder", drag: 'Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" })' }, { title: "web page", icon: "globe-asia", drag: 'Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" })' }, { title: "image", icon: "cat", drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { width: 200, title: "an image of a cat" })' }, { title: "button", icon: "bolt", drag: 'Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })' }, { title: "presentation", icon: "tv", drag: 'Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List(), { width: 200, height: 500, title: "a presentation trail" })' }, { title: "import folder", icon: "cloud-upload-alt", drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })' }, - { title: "pen", icon: "pen-nib", click: 'activatePen(); setInkWidth(2);' }, - { title: "highlighter", icon: "pen", click: 'activateBrush(); setInkWidth(20);' }, - { title: "eraser", icon: "eraser", click: 'activateEraser();' }, - { title: "none", icon: "pause", click: 'deactivateInk();' }, + { title: "pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", unchecked: `!sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "highlighter", icon: "pen", click: 'activateBrush(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", unchecked: `!sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', unchecked: `!sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "none", icon: "pause", click: 'deactivateInk();this.activePen.pen = this;', unchecked: `!sameDocs(this.activePen.pen, this)`, activePen: doc }, ]; return docProtoData.map(data => Docs.Create.FontIconDocument({ - nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.click ? "alias" : undefined, - title: data.title, icon: data.icon, onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, onClick: data.click ? ScriptField.MakeScript(data.click) : undefined + nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.click ? "copy" : undefined, title: data.title, icon: data.icon, + onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, + unchecked: data.unchecked ? ComputedField.MakeFunction(data.unchecked) : undefined, activePen: data.activePen, + backgroundColor: data.backgroundColor })); } // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker. when clicked, this panel will be displayed in the target container (ie, sidebarContainer) - static setupCreatePanel(sidebarContainer: Doc) { + static setupCreatePanel(sidebarContainer: Doc, doc: Doc) { // setup a masonry view of all he creators - const dragCreators = Docs.Create.MasonryDocument(CurrentUserUtils.setupCreatorButtons(), { + const dragCreators = Docs.Create.MasonryDocument(CurrentUserUtils.setupCreatorButtons(doc), { width: 500, autoHeight: true, columnWidth: 35, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons" }); // setup a color picker @@ -129,7 +132,7 @@ export class CurrentUserUtils { doc.sidebarContainer = new Doc(); (doc.sidebarContainer as Doc).chromeStatus = "disabled"; - doc.CreateBtn = this.setupCreatePanel(doc.sidebarContainer as Doc); + doc.CreateBtn = this.setupCreatePanel(doc.sidebarContainer as Doc, doc); doc.LibraryBtn = this.setupLibraryPanel(doc.sidebarContainer as Doc, doc); doc.SearchBtn = this.setupSearchPanel(doc.sidebarContainer as Doc); @@ -143,9 +146,9 @@ export class CurrentUserUtils { /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window static setupExpandingButtons(doc: Doc) { doc.undoBtn = Docs.Create.FontIconDocument( - { nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" }); + { nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: "alias", onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" }); doc.redoBtn = Docs.Create.FontIconDocument( - { nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" }); + { nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: "alias", onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" }); doc.expandingButtons = Docs.Create.LinearDocument([doc.undoBtn as Doc, doc.redoBtn as Doc], { title: "expanding buttons", gridGap: 5, xMargin: 5, yMargin: 5, height: 42, width: 100, boxShadow: "0 0", -- cgit v1.2.3-70-g09d2 From fc0473d0322c9d00ab339c5058dac5863a63ae41 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 15 Oct 2019 21:36:09 -0400 Subject: fixes for dropAction and buttons --- src/client/documents/Documents.ts | 2 +- src/server/authentication/models/current_user_utils.ts | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f4fce34ac..a5a9087c2 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -306,7 +306,7 @@ export namespace Docs { */ export namespace Create { - const delegateKeys = ["x", "y", "width", "height", "panX", "panY"]; + const delegateKeys = ["x", "y", "width", "height", "panX", "panY", "dropAction", "forceActive"]; /** * This function receives the relevant document prototype and uses diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 3c4a46ed8..0c9f09112 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -63,7 +63,7 @@ export class CurrentUserUtils { nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.click ? "copy" : undefined, title: data.title, icon: data.icon, onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, unchecked: data.unchecked ? ComputedField.MakeFunction(data.unchecked) : undefined, activePen: data.activePen, - backgroundColor: data.backgroundColor + backgroundColor: data.backgroundColor, removeDropProperties: new List(["dropAction"]) })); } @@ -75,10 +75,8 @@ export class CurrentUserUtils { }); // setup a color picker const color = Docs.Create.ColorDocument({ - title: "color picker", width: 400, removeDropProperties: new List(["dropAction", "forceActive"]) + title: "color picker", width: 400, dropAction: "alias", forceActive: true, removeDropProperties: new List(["dropAction", "forceActive"]) }); - color.dropAction = "alias"; // these must be set on the view document so they can't be part of the creator above. - color.forceActive = true; return Docs.Create.ButtonDocument({ width: 35, height: 35, borderRounding: "50%", boxShadow: "2px 2px 1px", title: "Create", targetContainer: sidebarContainer, @@ -152,7 +150,7 @@ export class CurrentUserUtils { doc.expandingButtons = Docs.Create.LinearDocument([doc.undoBtn as Doc, doc.redoBtn as Doc], { title: "expanding buttons", gridGap: 5, xMargin: 5, yMargin: 5, height: 42, width: 100, boxShadow: "0 0", - backgroundColor: "black", preventTreeViewOpen: true, forceActive: true, lockedPosition: true, convertToButtons: true + backgroundColor: "black", preventTreeViewOpen: true, forceActive: true, lockedPosition: true, convertToButtons: true, }); } -- cgit v1.2.3-70-g09d2 From 789b4925dff316ed9151bf9d327ab6223f09ec4a Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 16 Oct 2019 17:07:42 -0400 Subject: added a default note template --- src/client/documents/Documents.ts | 1 + src/server/authentication/models/current_user_utils.ts | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a5a9087c2..ccb08f4cd 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -96,6 +96,7 @@ export interface DocumentOptions { unchecked?: ScriptField; // returns whether a check box is unchecked activePen?: Doc; // which pen document is currently active (used as the radio button state for the 'unhecked' pen tool scripts) onClick?: ScriptField; + dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop icon?: string; gridGap?: number; // gap between items in masonry view diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 0fe8d996f..ef977c89a 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -35,20 +35,23 @@ export class CurrentUserUtils { // a default set of note types .. not being used yet... static setupNoteTypes(doc: Doc) { - let notes = [ + return [ Docs.Create.TextDocument({ title: "Note", backgroundColor: "yellow", isTemplate: true }), Docs.Create.TextDocument({ title: "Idea", backgroundColor: "pink", isTemplate: true }), Docs.Create.TextDocument({ title: "Topic", backgroundColor: "lightBlue", isTemplate: true }), - Docs.Create.TextDocument({ title: "Person", backgroundColor: "lightGreen", isTemplate: true }) + Docs.Create.TextDocument({ title: "Person", backgroundColor: "lightGreen", isTemplate: true }), + Docs.Create.TextDocument({ title: "Todo", backgroundColor: "orange", isTemplate: true }) ]; - doc.noteTypes = Docs.Create.TreeDocument(notes, { title: "Note Types", height: 75 }); } // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools static setupCreatorButtons(doc: Doc) { + let notes = CurrentUserUtils.setupNoteTypes(doc); + doc.noteTypes = Docs.Create.TreeDocument(notes, { title: "Note Types", height: 75 }); doc.activePen = doc; - let docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, unchecked?: string, activePen?: Doc, backgroundColor?: string }[] = [ + let docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, unchecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ { title: "collection", icon: "folder", ignoreClick: true, drag: 'Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" })' }, + { title: "todo item", icon: "check", ignoreClick: true, drag: 'getCopy(this.dragFactory, true)', dragFactory: notes[notes.length - 1] }, { title: "web page", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" })' }, { title: "cat image", icon: "cat", ignoreClick: true, drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { width: 200, title: "an image of a cat" })' }, { title: "clickable button", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })' }, @@ -63,7 +66,7 @@ export class CurrentUserUtils { nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.click ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, unchecked: data.unchecked ? ComputedField.MakeFunction(data.unchecked) : undefined, activePen: data.activePen, - backgroundColor: data.backgroundColor, removeDropProperties: new List(["dropAction"]) + backgroundColor: data.backgroundColor, removeDropProperties: new List(["dropAction"]), dragFactory: data.dragFactory })); } @@ -173,7 +176,6 @@ export class CurrentUserUtils { static updateUserDocument(doc: Doc) { new InkingControl(); (doc.optionalRightCollection === undefined) && CurrentUserUtils.setupMobileUploads(doc); - (doc.noteTypes === undefined) && CurrentUserUtils.setupNoteTypes(doc); (doc.overlays === undefined) && CurrentUserUtils.setupOverlays(doc); (doc.expandingButtons === undefined) && CurrentUserUtils.setupExpandingButtons(doc); (doc.curPresentation === undefined) && CurrentUserUtils.setupDefaultPresentation(doc); -- cgit v1.2.3-70-g09d2 From 7763a08eb5ed931dbf854e2b72d07b7613791e2b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 16 Oct 2019 21:27:25 -0400 Subject: some fixes to linearView for handling templates. fixed DOCUMENTS on library view. renaming a bunch of template stuff... still in progress. --- src/client/documents/Documents.ts | 1 + src/client/views/CollectionLinearView.tsx | 18 ++++++++++++++---- src/client/views/DocComponent.tsx | 2 +- src/client/views/DocumentDecorations.tsx | 4 ++-- src/client/views/InkingControl.tsx | 7 ++----- src/client/views/TemplateMenu.tsx | 2 +- src/client/views/collections/CollectionBaseView.tsx | 10 +++++----- .../views/collections/CollectionStackingView.tsx | 4 +++- src/client/views/collections/CollectionSubView.tsx | 11 ++++++++--- src/client/views/collections/CollectionTreeView.tsx | 3 ++- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/ButtonBox.tsx | 6 +++++- src/client/views/nodes/DocumentView.tsx | 6 +++--- src/client/views/nodes/FormattedTextBox.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 2 +- src/new_fields/Doc.ts | 6 +++--- src/server/authentication/models/current_user_utils.ts | 16 ++++++++-------- 17 files changed, 61 insertions(+), 41 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ccb08f4cd..2ffbc8394 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -70,6 +70,7 @@ export interface DocumentOptions { layout?: string | Doc; hideHeadings?: boolean; // whether stacking view column headings should be hidden isTemplate?: boolean; + isTemplateDoc?: boolean; templates?: List; viewType?: number; backgroundColor?: string; diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx index 44d9b042e..3e2ab1459 100644 --- a/src/client/views/CollectionLinearView.tsx +++ b/src/client/views/CollectionLinearView.tsx @@ -1,7 +1,7 @@ import { action, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, HeightSym, WidthSym } from '../../new_fields/Doc'; +import { Doc, HeightSym, WidthSym, DocListCast } from '../../new_fields/Doc'; import { ObjectField } from '../../new_fields/ObjectField'; import { makeInterface } from '../../new_fields/Schema'; import { ScriptField } from '../../new_fields/ScriptField'; @@ -51,9 +51,19 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { (de.data as DragManager.DocumentDragData).draggedDocuments.map((doc, i) => { let dbox = doc; if (!doc.onDragStart && !doc.onClick && this.props.Document.convertToButtons && doc.viewType !== CollectionViewType.Linear) { - let template = doc.layout instanceof Doc && doc.layout.isTemplate ? doc.layout : doc; - template.isTemplate = (template.type === DocumentType.TEXT || template.layout instanceof Doc) && de.data instanceof DragManager.DocumentDragData && !de.data.userDropAction; - dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: template.isTemplate ? "font" : "bolt" }); + let template = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc; + if (template.type === DocumentType.COL) { + let layout = StrCast(template.layout).match(/fieldKey={"[^"]*"}/)![0]; + let fieldKey = layout.replace('fieldKey={"', "").replace(/"}$/, ""); + let docs = DocListCast(template[fieldKey]); + docs.map(d => { + Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(template)); + }); + template.isTemplateDoc = true; + } else { + template.isTemplateDoc = (template.type === DocumentType.TEXT || template.layout instanceof Doc) && de.data instanceof DragManager.DocumentDragData && !de.data.userDropAction; + } + dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: template.isTemplateDoc ? "font" : "bolt" }); dbox.dragFactory = template; dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined; dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index fbc27192c..b6b717be0 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -68,7 +68,7 @@ export function DocAnnotatableComponent

    (schema return index !== -1 && value.splice(index, 1) ? true : false; } - @computed get dataDoc() { return (this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; } + @computed get dataDoc() { return (this.props.DataDoc && this.props.Document.isTemplateField ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; } @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 2f40ea746..6e8ba2d3d 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -380,7 +380,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> ruleProvider && heading && (Doc.GetProto(ruleProvider)["ruleRounding_" + heading] = `${Math.min(100, dist)}%`); usingRule = usingRule || (ruleProvider && heading ? true : false); }); - !usingRule && SelectionManager.SelectedDocuments().map(dv => dv.props.Document.layout instanceof Doc ? dv.props.Document.layout : dv.props.Document.isTemplate ? dv.props.Document : Doc.GetProto(dv.props.Document)). + !usingRule && SelectionManager.SelectedDocuments().map(dv => dv.props.Document.layout instanceof Doc ? dv.props.Document.layout : dv.props.Document.isTemplateField ? dv.props.Document : Doc.GetProto(dv.props.Document)). map(d => d.borderRounding = `${Math.min(100, dist)}%`); e.stopPropagation(); e.preventDefault(); @@ -481,7 +481,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let actualdH = Math.max(height + (dH * scale), 20); layoutDoc.x = (layoutDoc.x || 0) + dX * (actualdW - width); layoutDoc.y = (layoutDoc.y || 0) + dY * (actualdH - height); - let proto = doc.isTemplate ? doc : Doc.GetProto(element.props.Document); // bcz: 'doc' didn't work here... + let proto = doc.isTemplateField ? doc : Doc.GetProto(element.props.Document); // bcz: 'doc' didn't work here... let fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight); if (fixedAspect && e.ctrlKey && layoutDoc.ignoreAspect) { layoutDoc.ignoreAspect = false; diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 46c6fae1c..bc5249acd 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -43,7 +43,7 @@ export class InkingControl { let oldColors = selected.map(view => { let targetDoc = view.props.Document.dragFactory instanceof Doc ? view.props.Document.dragFactory : view.props.Document.layout instanceof Doc ? view.props.Document.layout : - view.props.Document.isTemplate ? view.props.Document : Doc.GetProto(view.props.Document); + view.props.Document.isTemplateField ? view.props.Document : Doc.GetProto(view.props.Document); let sel = window.getSelection(); if (StrCast(targetDoc.layout).indexOf("FormattedTextBox") !== -1 && (!sel || sel.toString() !== "")) { targetDoc.color = this._selectedColor; @@ -81,10 +81,7 @@ export class InkingControl { ruleProvider = (view.props.Document.heading && ruleProvider) ? ruleProvider : undefined; ruleProvider && ((Doc.GetProto(ruleProvider)["ruleColor_" + NumCast(view.props.Document.heading)] = Utils.toRGBAstr(color.rgb))); } - if (!ruleProvider) { - if (targetDoc) - targetDoc.backgroundColor = matchedColor; - } + (!ruleProvider && targetDoc) && (targetDoc.backgroundColor = matchedColor); return { target: targetDoc, diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 9e5e62e03..da776f887 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -100,7 +100,7 @@ export class TemplateMenu extends React.Component { clearTemplates = (event: React.MouseEvent) => { Templates.TemplateList.forEach(template => this.props.docs.forEach(d => d.Document["show" + template.Name] = undefined)); ["backgroundColor", "borderRounding", "width", "height"].forEach(field => this.props.docs.forEach(d => { - if (d.Document.isTemplate && d.props.DataDoc) { + if (d.Document.isTemplateDoc && d.props.DataDoc) { d.Document[field] = undefined; } else if (d.Document["default" + field[0].toUpperCase() + field.slice(1)] !== undefined) { d.Document[field] = Doc.GetProto(d.Document)[field] = undefined; diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 7798964ea..15853fcae 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -84,7 +84,7 @@ export class CollectionBaseView extends React.Component { } } - @computed get dataDoc() { return Doc.fieldExtensionDoc(this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); } + @computed get dataDoc() { return Doc.fieldExtensionDoc(this.props.Document.isTemplateField && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); } @computed get dataField() { return this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey; } active = (): boolean => { @@ -106,8 +106,8 @@ export class CollectionBaseView extends React.Component { if (this.props.fieldExt) { // bcz: fieldExt !== undefined means this is an overlay layer Doc.GetProto(doc).annotationOn = this.props.Document; } - 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 targetDataDoc = this.props.fieldExt || this.props.Document.isTemplateField ? this.extensionDoc : this.props.Document; + let targetField = (this.props.fieldExt || this.props.Document.isTemplateField) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey; Doc.AddDocToList(targetDataDoc, targetField, doc); Doc.GetProto(doc).lastOpened = new DateField; return true; @@ -118,8 +118,8 @@ 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 - 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 targetDataDoc = this.props.fieldExt || this.props.Document.isTemplateField ? this.extensionDoc : this.props.Document; + let targetField = (this.props.fieldExt || this.props.Document.isTemplateField) && 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 === doc) ? i : p, -1); index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index cde1a5036..e54374ad7 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -110,6 +110,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } componentDidMount() { + super.componentDidMount(); this._heightDisposer = reaction(() => { if (this.props.Document.autoHeight) { let sectionsList = Array.from(this.Sections.size ? this.Sections.values() : [this.filteredChildren]); @@ -137,6 +138,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { ); } componentWillUnmount() { + super.componentWillUnmount(); this._heightDisposer && this._heightDisposer(); this._sectionFilterDisposer && this._sectionFilterDisposer(); } @@ -167,7 +169,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { renderDepth={this.props.renderDepth} ruleProvider={this.props.Document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider} fitToBox={this.props.fitToBox} - onClick={layoutDoc.isTemplate ? this.onClickHandler : this.onChildClickHandler} + onClick={layoutDoc.isTemplateDoc ? this.onClickHandler : this.onChildClickHandler} PanelWidth={width} PanelHeight={height} getTransform={finalDxf} diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 9919a9dc3..46fbb5910 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -57,8 +57,13 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { componentDidMount() { this._childLayoutDisposer = reaction(() => [this.childDocs, Cast(this.props.Document.childLayout, Doc)], - async (args) => args[1] instanceof Doc && - this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc)))); + async (args) => { + if (args[1] instanceof Doc) { + this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc))); + } else { + this.childDocs.map(async doc => doc.layout = undefined); + } + }); } componentWillUnmount() { @@ -70,7 +75,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { // to its children which may be templates. // The name of the data field comes from fieldExt if it's an extension, or fieldKey otherwise. @computed get dataField() { - return Doc.fieldExtensionDoc(this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt)[this.props.fieldExt || this.props.fieldKey]; + return Doc.fieldExtensionDoc(this.props.Document.isTemplateField && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt)[this.props.fieldExt || this.props.fieldKey]; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 403da0e54..0e9c38fb4 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -520,7 +520,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { private treedropDisposer?: DragManager.DragDropDisposer; private _mainEle?: HTMLDivElement; - @computed get resolvedDataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document; } + @computed get resolvedDataDoc() { return BoolCast(this.props.Document.isTemplateField) && this.props.DataDoc ? this.props.DataDoc : this.props.Document; } protected createTreeDropTarget = (ele: HTMLDivElement) => { this.treedropDisposer && this.treedropDisposer(); @@ -530,6 +530,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { } componentWillUnmount() { + super.componentWillUnmount(); this.treedropDisposer && this.treedropDisposer(); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 229e7fffc..2b0ef8ada 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -627,7 +627,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { onContextMenu = (e: React.MouseEvent) => { let layoutItems: ContextMenuProps[] = []; - if (this.childDocs.some(d => BoolCast(d.isTemplate))) { + if (this.childDocs.some(d => BoolCast(d.isTemplateDoc))) { layoutItems.push({ description: "Template Layout Instance", event: () => this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" }); } layoutItems.push({ description: "reset view", event: () => { this.props.Document.panX = this.props.Document.panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" }); diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx index 3cf8c3eb3..b4d33fb0f 100644 --- a/src/client/views/nodes/ButtonBox.tsx +++ b/src/client/views/nodes/ButtonBox.tsx @@ -32,7 +32,11 @@ export class ButtonBox extends DocComponent(Butt public static LayoutString() { return FieldView.LayoutString(ButtonBox); } private dropDisposer?: DragManager.DragDropDisposer; - @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); } + @computed get dataDoc() { + return this.props.DataDoc && + (BoolCast(this.props.Document.isTemplateField) || BoolCast(this.props.DataDoc.isTemplateField) || + this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); + } protected createDropTarget = (ele: HTMLDivElement) => { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a071f782a..a7e78a7e8 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -110,7 +110,7 @@ export const documentSchema = createSchema({ dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document. ignoreAspect: "boolean", // whether aspect ratio should be ignored when laying out or manipulating the document autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents - isTemplate: "boolean", // whether this document acts as a template layout for describing how other documents should be displayed + isTemplateField: "boolean", // whether this document acts as a template layout for describing how other documents should be displayed isBackground: "boolean", // whether document is a background element and ignores input events (can only selet with marquee) type: "string", // enumerated type of document maximizeLocation: "string", // flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab) @@ -196,7 +196,7 @@ export class DocumentView extends DocComponent(Docu SelectionManager.DeselectAll(); Doc.UnBrushDoc(this.props.Document); } else if (this.onClickHandler && this.onClickHandler.script) { - this.onClickHandler.script.run({ this: this.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document }, console.log); + this.onClickHandler.script.run({ this: this.Document.isTemplateField && this.props.DataDoc ? this.props.DataDoc : this.props.Document }, console.log); } else if (this.props.Document.type === DocumentType.BUTTON) { ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", e.clientX, e.clientY); } else if (this.Document.isButton) { @@ -376,7 +376,7 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action freezeNativeDimensions = (): void => { - let proto = this.Document.isTemplate ? this.props.Document : Doc.GetProto(this.props.Document); + let proto = this.Document.isTemplateDoc ? this.props.Document : Doc.GetProto(this.props.Document); proto.autoHeight = this.Document.autoHeight = false; proto.ignoreAspect = !proto.ignoreAspect; if (!proto.ignoreAspect && !proto.nativeWidth) { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index f157a953e..ea82b1161 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -993,7 +993,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let scrollHeight = this._ref.current ? this._ref.current.scrollHeight : 0; if (!this.layoutDoc.isAnimating && this.layoutDoc.autoHeight && scrollHeight !== 0 && getComputedStyle(this._ref.current!.parentElement!).top === "0px") { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation - let nh = this.props.Document.isTemplate ? 0 : NumCast(this.dataDoc.nativeHeight, 0); + let nh = this.props.Document.isTemplateField ? 0 : NumCast(this.dataDoc.nativeHeight, 0); let dh = NumCast(this.layoutDoc.height, 0); this.layoutDoc.height = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0)); this.dataDoc.nativeHeight = nh ? scrollHeight : undefined; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index aa9b28118..19968e6e1 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -323,7 +323,7 @@ export class VideoBox extends DocAnnotatableComponent(field.map((d: any) => Doc.MakeAlias(d))); }); Scripting.addGlobal(function docList(field: any) { return DocListCast(field); }); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index ef977c89a..5ce707011 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -36,11 +36,11 @@ export class CurrentUserUtils { // a default set of note types .. not being used yet... static setupNoteTypes(doc: Doc) { return [ - Docs.Create.TextDocument({ title: "Note", backgroundColor: "yellow", isTemplate: true }), - Docs.Create.TextDocument({ title: "Idea", backgroundColor: "pink", isTemplate: true }), - Docs.Create.TextDocument({ title: "Topic", backgroundColor: "lightBlue", isTemplate: true }), - Docs.Create.TextDocument({ title: "Person", backgroundColor: "lightGreen", isTemplate: true }), - Docs.Create.TextDocument({ title: "Todo", backgroundColor: "orange", isTemplate: true }) + Docs.Create.TextDocument({ title: "Note", backgroundColor: "yellow", isTemplateDoc: true }), + Docs.Create.TextDocument({ title: "Idea", backgroundColor: "pink", isTemplateDoc: true }), + Docs.Create.TextDocument({ title: "Topic", backgroundColor: "lightBlue", isTemplateDoc: true }), + Docs.Create.TextDocument({ title: "Person", backgroundColor: "lightGreen", isTemplateDoc: true }), + Docs.Create.TextDocument({ title: "Todo", backgroundColor: "orange", isTemplateDoc: true }) ]; } @@ -98,17 +98,17 @@ export class CurrentUserUtils { }); doc.documents = Docs.Create.TreeDocument([], { - title: "DOCUMENTS", gridGap: 5, xMargin: 5, yMargin: 5, height: 42, width: 100, boxShadow: "0 0", backgroundColor: "#eeeeee", preventTreeViewOpen: true, forceActive: true, lockedPosition: true + title: "DOCUMENTS", height: 42, forceActive: true, boxShadow: "0 0", preventTreeViewOpen: true, lockedPosition: true, backgroundColor: "#eeeeee" }); // setup Recently Closed library item doc.recentlyClosed = Docs.Create.TreeDocument([], { - title: "Recently Closed".toUpperCase(), height: 75, boxShadow: "0 0", preventTreeViewOpen: true, forceActive: true, lockedPosition: true, backgroundColor: "#eeeeee" + title: "RECENTLY CLOSED", height: 75, forceActive: true, boxShadow: "0 0", preventTreeViewOpen: true, lockedPosition: true, backgroundColor: "#eeeeee" }); return Docs.Create.ButtonDocument({ width: 50, height: 35, borderRounding: "50%", boxShadow: "2px 2px 1px", title: "Library", - panel: Docs.Create.TreeDocument([doc.workspaces as Doc, doc, doc.recentlyClosed as Doc], { + panel: Docs.Create.TreeDocument([doc.workspaces as Doc, doc.documents as Doc, doc.recentlyClosed as Doc], { title: "Library", xMargin: 5, yMargin: 5, gridGap: 5, forceActive: true, dropAction: "alias", lockedPosition: true }), targetContainer: sidebarContainer, -- cgit v1.2.3-70-g09d2 From caebaeca6993ff48609ab16dcc390d4d0458b03a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 16 Oct 2019 23:50:27 -0400 Subject: fixed applying templates to collections. fixed templates containing collections --- src/client/documents/Documents.ts | 2 +- src/client/views/CollectionLinearView.tsx | 35 ++++++++++++++-------- src/client/views/InkingCanvas.tsx | 3 +- src/client/views/InkingControl.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 2 +- .../views/collections/CollectionViewChromes.tsx | 4 +-- src/client/views/nodes/ColorBox.tsx | 7 ++--- src/client/views/nodes/FormattedTextBox.tsx | 4 +-- src/new_fields/Doc.ts | 14 +++++++-- .../authentication/models/current_user_utils.ts | 14 +++------ 10 files changed, 51 insertions(+), 36 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2ffbc8394..62175cbe3 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -69,7 +69,7 @@ export interface DocumentOptions { preventTreeViewOpen?: boolean; // ignores the treeViewOpen Doc flag which allows a treeViewItem's expande/collapse state to be independent of other views of the same document in the tree view layout?: string | Doc; hideHeadings?: boolean; // whether stacking view column headings should be hidden - isTemplate?: boolean; + isTemplateField?: boolean; isTemplateDoc?: boolean; templates?: List; viewType?: number; diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx index 3e2ab1459..04e131135 100644 --- a/src/client/views/CollectionLinearView.tsx +++ b/src/client/views/CollectionLinearView.tsx @@ -47,24 +47,35 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { } } + makeTemplate = (doc: Doc): boolean => { + let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc; + let layout = StrCast(layoutDoc.layout).match(/fieldKey={"[^"]*"}/)![0]; + let fieldKey = layout.replace('fieldKey={"', "").replace(/"}$/, ""); + let docs = DocListCast(layoutDoc[fieldKey]); + let any = false; + docs.map(d => { + if (!StrCast(d.title).startsWith("-")) { + any = true; + return Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc)); + } + if (d.type === DocumentType.COL) return this.makeTemplate(d); + return false; + }); + return any; + } + drop = action((e: Event, de: DragManager.DropEvent) => { (de.data as DragManager.DocumentDragData).draggedDocuments.map((doc, i) => { let dbox = doc; if (!doc.onDragStart && !doc.onClick && this.props.Document.convertToButtons && doc.viewType !== CollectionViewType.Linear) { - let template = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc; - if (template.type === DocumentType.COL) { - let layout = StrCast(template.layout).match(/fieldKey={"[^"]*"}/)![0]; - let fieldKey = layout.replace('fieldKey={"', "").replace(/"}$/, ""); - let docs = DocListCast(template[fieldKey]); - docs.map(d => { - Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(template)); - }); - template.isTemplateDoc = true; + let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc; + if (layoutDoc.type === DocumentType.COL) { + layoutDoc.isTemplateDoc = this.makeTemplate(layoutDoc); } else { - template.isTemplateDoc = (template.type === DocumentType.TEXT || template.layout instanceof Doc) && de.data instanceof DragManager.DocumentDragData && !de.data.userDropAction; + layoutDoc.isTemplateDoc = (layoutDoc.type === DocumentType.TEXT || layoutDoc.layout instanceof Doc) && de.data instanceof DragManager.DocumentDragData && !de.data.userDropAction; } - dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: template.isTemplateDoc ? "font" : "bolt" }); - dbox.dragFactory = template; + dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: layoutDoc.isTemplateDoc ? "font" : "bolt" }); + dbox.dragFactory = layoutDoc; dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined; dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); } else if (doc.viewType === CollectionViewType.Linear) { diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index 9ab320eab..920ebaedd 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -181,9 +181,10 @@ export class InkingCanvas extends React.Component { render() { let svgCanvasStyle = InkingControl.Instance.selectedTool !== InkTool.None && !this.props.Document.isBackground ? "canSelect" : "noSelect"; + let cursor = svgCanvasStyle === "canSelect" ? (InkingControl.Instance.selectedTool === InkTool.Eraser ? "pointer" : "default") : undefined; return (

    -
    +
    {this.props.children()} {this.drawnPaths}
    diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index bc5249acd..b8f3c2875 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -94,7 +94,7 @@ export class InkingControl { redo: () => oldColors.forEach(pair => pair.target.backgroundColor = captured) }); } else { - CurrentUserUtils.UserDocument.activePen instanceof Doc && CurrentUserUtils.UserDocument.activePen.pen instanceof Doc && (CurrentUserUtils.UserDocument.activePen.pen.backgroundColor = this._selectedColor); + CurrentUserUtils.ActivePen && (CurrentUserUtils.ActivePen.backgroundColor = this._selectedColor); } }); @action diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 46fbb5910..1f8c0b18f 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -61,7 +61,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { if (args[1] instanceof Doc) { this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc))); } else { - this.childDocs.map(async doc => doc.layout = undefined); + this.childDocs.filter(d => !d.isTemplateField).map(async doc => doc.layout = undefined); } }); diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 3a66c05f4..a5b7f0181 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -40,9 +40,9 @@ export class CollectionViewBaseChrome extends React.Component this.props.CollectionView.props.Document.childLayout = draggedDocs.length ? draggedDocs[0] : undefined + immediate: (draggedDocs: Doc[]) => Doc.setChildLayout(this.props.CollectionView.props.Document, draggedDocs.length ? draggedDocs[0] : undefined) }; _contentCommand = { // title: "set content", script: "getProto(this.target).data = aliasDocs(this.source.map(async p => await p));", params: ["target", "source"], // bcz: doesn't look like we can do async stuff in scripting... diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx index e4fef0922..30554ea36 100644 --- a/src/client/views/nodes/ColorBox.tsx +++ b/src/client/views/nodes/ColorBox.tsx @@ -25,9 +25,8 @@ export class ColorBox extends DocStaticComponent( this._selectedDisposer = reaction(() => SelectionManager.SelectedDocuments(), action(() => this._startupColor = SelectionManager.SelectedDocuments().length ? StrCast(SelectionManager.SelectedDocuments()[0].Document.backgroundColor, "black") : "black"), { fireImmediately: true }); - this._penDisposer = reaction(() => CurrentUserUtils.UserDocument.activePen instanceof Doc && CurrentUserUtils.UserDocument.activePen.pen, - action(() => this._startupColor = CurrentUserUtils.UserDocument.activePen instanceof Doc && CurrentUserUtils.UserDocument.activePen.pen instanceof Doc ? - StrCast(CurrentUserUtils.UserDocument.activePen.pen.backgroundColor, "black") : "black"), + this._penDisposer = reaction(() => CurrentUserUtils.ActivePen, + action(() => this._startupColor = CurrentUserUtils.ActivePen ? StrCast(CurrentUserUtils.ActivePen.backgroundColor, "black") : "black"), { fireImmediately: true }); // compare to this reaction that used to be in Selection Manager @@ -36,7 +35,7 @@ export class ColorBox extends DocStaticComponent( // if (sel.length > 0) { // let firstView = sel[0]; // let doc = firstView.props.Document; - // let targetDoc = doc.isTemplate ? doc : Doc.GetProto(doc); + // let targetDoc = doc.isTemplateField ? doc : Doc.GetProto(doc); // let stored = StrCast(targetDoc.backgroundColor); // stored.length > 0 && (targetColor = stored); // } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index ea82b1161..045eee3f7 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -143,7 +143,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } - @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? Doc.GetProto(this.props.DataDoc) : Doc.GetProto(this.props.Document); } + @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplateField ? Doc.GetProto(this.props.DataDoc) : Doc.GetProto(this.props.Document); } // the document containing the view layout information - will be the Document itself unless the Document has // a layout field. In that case, all layout information comes from there unless overriden by Document @@ -265,7 +265,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } // apply as template when dragging with Meta } else if (draggedDoc && draggedDoc.type === DocumentType.TEXT && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.mods === "MetaKey") { - draggedDoc.isTemplate = true; + draggedDoc.isTemplateDoc = true; let newLayout = draggedDoc.layout instanceof Doc ? draggedDoc.layout : draggedDoc; if (typeof (draggedDoc.layout) === "string") { newLayout = Doc.MakeDelegate(draggedDoc); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 51fcb818a..04f310785 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -599,7 +599,7 @@ export namespace Doc { return target; } - export function MakeMetadataFieldTemplate(fieldTemplate: Doc, templateDataDoc: Doc, suppressTitle: boolean = false) { + export function MakeMetadataFieldTemplate(fieldTemplate: Doc, templateDataDoc: Doc, suppressTitle: boolean = false): boolean { // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??) let metadataFieldName = StrCast(fieldTemplate.title).replace(/^-/, ""); let backgroundLayout = StrCast(fieldTemplate.backgroundLayout); @@ -613,7 +613,7 @@ export namespace Doc { fieldTemplate.templateField = metadataFieldName; fieldTemplate.title = metadataFieldName; - fieldTemplate.isTemplate = true; + fieldTemplate.isTemplateField = true; fieldTemplate.backgroundLayout = backgroundLayout; /* move certain layout properties from the original data doc to the template layout to avoid inheriting them from the template's data doc which may also define these fields for its own use. @@ -636,6 +636,7 @@ export namespace Doc { if (fieldTemplate.backgroundColor !== templateDataDoc.defaultBackgroundColor) fieldTemplate.defaultBackgroundColor = fieldTemplate.backgroundColor; fieldTemplate.proto = templateDataDoc; }), 0); + return true; } export function overlapping(doc: Doc, doc2: Doc, clusterDistance: number) { @@ -735,9 +736,18 @@ export namespace Doc { export function UnBrushAllDocs() { manager.BrushedDoc.clear(); } + + export function setChildLayout(target: Doc, source?: Doc) { + target.childLayout = source && source.isTemplateDoc ? source : source && + source.dragFactory instanceof Doc && source.dragFactory.isTemplateDoc ? source.dragFactory : + source && source.layout instanceof Doc && source.layout.isTemplateDoc ? source.layout : undefined; + } } + + Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; }); Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); }); +Scripting.addGlobal(function setChildLayout(target: any, source: any) { Doc.setChildLayout(target, source); }); Scripting.addGlobal(function getAlias(doc: any) { return Doc.MakeAlias(doc); }); Scripting.addGlobal(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); }); Scripting.addGlobal(function copyField(field: any) { return ObjectField.MakeCopy(field); }); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 5ce707011..e6f202685 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -23,16 +23,11 @@ export class CurrentUserUtils { public static get MainDocId() { return this.mainDocId; } public static set MainDocId(id: string | undefined) { this.mainDocId = id; } @computed public static get UserDocument() { return Doc.UserDoc(); } + @computed public static get ActivePen() { return Doc.UserDoc().activePen instanceof Doc && (Doc.UserDoc().activePen as Doc).pen as Doc; } @observable public static GuestTarget: Doc | undefined; @observable public static GuestWorkspace: Doc | undefined; - private static createUserDocument(id: string): Doc { - let doc = new Doc(id, true); - doc.title = Doc.CurrentUserEmail; - return this.updateUserDocument(doc);// this should be the last - } - // a default set of note types .. not being used yet... static setupNoteTypes(doc: Doc) { return [ @@ -174,6 +169,7 @@ export class CurrentUserUtils { } static updateUserDocument(doc: Doc) { + doc.title = Doc.CurrentUserEmail; new InkingControl(); (doc.optionalRightCollection === undefined) && CurrentUserUtils.setupMobileUploads(doc); (doc.overlays === undefined) && CurrentUserUtils.setupOverlays(doc); @@ -216,10 +212,8 @@ export class CurrentUserUtils { Doc.CurrentUserEmail = email; await rp.get(Utils.prepend(RouteStore.getUserDocumentId)).then(id => { if (id && id !== "guest") { - return DocServer.GetRefField(id).then(async field => { - let userDoc = field instanceof Doc ? await this.updateUserDocument(field) : this.createUserDocument(id); - runInAction(() => Doc.SetUserDoc(userDoc)); - }); + return DocServer.GetRefField(id).then(async field => + Doc.SetUserDoc(await this.updateUserDocument(field instanceof Doc ? field : new Doc(id, true)))); } 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 fca8d503610f799ca0e4afcec114075456d411e0 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 17 Oct 2019 16:48:39 -0400 Subject: switched links over to be a document --- src/Utils.ts | 2 + src/client/documents/DocumentTypes.ts | 1 + src/client/documents/Documents.ts | 5 ++ src/client/views/DocumentDecorations.tsx | 3 +- src/client/views/InkingControl.tsx | 2 +- src/client/views/nodes/DocuLinkBox.tsx | 77 ++++++++++++++++++++++ src/client/views/nodes/DocuLinkView.tsx | 85 ------------------------- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 31 ++------- 9 files changed, 94 insertions(+), 115 deletions(-) create mode 100644 src/client/views/nodes/DocuLinkBox.tsx delete mode 100644 src/client/views/nodes/DocuLinkView.tsx (limited to 'src/client/documents') diff --git a/src/Utils.ts b/src/Utils.ts index 66ed8be5d..7bb025e49 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -289,6 +289,8 @@ export function percent2frac(percent: string) { export function numberRange(num: number) { return Array.from(Array(num)).map((v, i) => i); } +export function returnTransparent() { return "transparent"; } + export function returnTrue() { return true; } export function returnFalse() { return false; } diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 03178bbdb..ea37fc2f1 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -23,4 +23,5 @@ export enum DocumentType { PRESELEMENT = "preselement", QUERY = "search", COLOR = "color", + DOCULINK = "doculink" } \ No newline at end of file diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 62175cbe3..edc2ac653 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -47,6 +47,7 @@ import { LinkFollowBox } from "../views/linking/LinkFollowBox"; import { PresElementBox } from "../views/presentationview/PresElementBox"; import { QueryBox } from "../views/nodes/QueryBox"; import { ColorBox } from "../views/nodes/ColorBox"; +import { DocuLinkBox } from "../views/nodes/DocuLinkBox"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -719,6 +720,10 @@ export namespace DocUtils { linkDocProto.anchor2Context = target.ctx; linkDocProto.anchor2Groups = new List([]); linkDocProto.anchor2Timecode = target.doc.currentTimecode; + linkDocProto.layoutKey1 = DocuLinkBox.LayoutString("anchor1"); + linkDocProto.layoutKey2 = DocuLinkBox.LayoutString("anchor2"); + linkDocProto.width = linkDocProto.height = 0; + linkDocProto.isBackground = true; LinkManager.Instance.addLink(linkDocProto); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 927729487..6e8ba2d3d 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -167,8 +167,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> get Bounds(): { x: number, y: number, b: number, r: number } { let x = this._forceUpdate; this._lastBox = SelectionManager.SelectedDocuments().reduce((bounds, documentView) => { - if (documentView._selectedLink !== -1 || - documentView.props.renderDepth === 0 || + if (documentView.props.renderDepth === 0 || Doc.AreProtosEqual(documentView.props.Document, CurrentUserUtils.UserDocument)) { return bounds; } diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 1aacdc8ec..c3a617ffe 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -81,7 +81,7 @@ export class InkingControl { ruleProvider = (view.props.Document.heading && ruleProvider) ? ruleProvider : undefined; ruleProvider && ((Doc.GetProto(ruleProvider)["ruleColor_" + NumCast(view.props.Document.heading)] = Utils.toRGBAstr(color.rgb))); } - (!ruleProvider && targetDoc) && (view.setBackround(matchedColor)); + (!ruleProvider && targetDoc) && (view.props.Document.backgroundColor = matchedColor); return { target: targetDoc, diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx new file mode 100644 index 000000000..2e968a9f6 --- /dev/null +++ b/src/client/views/nodes/DocuLinkBox.tsx @@ -0,0 +1,77 @@ +import { action, observable } from "mobx"; +import { observer } from "mobx-react"; +import { Doc } from "../../../new_fields/Doc"; +import { makeInterface } from "../../../new_fields/Schema"; +import { NumCast, StrCast } from "../../../new_fields/Types"; +import { Utils } from '../../../Utils'; +import { DocumentManager } from "../../util/DocumentManager"; +import { DragLinksAsDocuments } from "../../util/DragManager"; +import { DocComponent } from "../DocComponent"; +import { documentSchema } from "./DocumentView"; +import "./DocumentView.scss"; +import { FieldView, FieldViewProps } from "./FieldView"; +import React = require("react"); + +type DocLinkSchema = makeInterface<[typeof documentSchema]>; +const DocLinkDocument = makeInterface(documentSchema); + +@observer +export class DocuLinkBox extends DocComponent(DocLinkDocument) { + public static LayoutString(fieldKey: string, fieldExt?: string) { return FieldView.LayoutString(DocuLinkBox, fieldKey, fieldExt); } + _downx = 0; + _downy = 0; + @observable _x = 0; + @observable _y = 0; + @observable _selected = false; + _ref = React.createRef(); + + onPointerDown = (e: React.PointerEvent) => { + this._downx = e.clientX; + this._downy = e.clientY; + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointerup", this.onPointerUp); + e.stopPropagation(); + } + onPointerMove = action((e: PointerEvent) => { + let cdiv = this._ref.current!.parentElement; + if (cdiv && (Math.abs(e.clientX - this._downx) > 5 || Math.abs(e.clientY - this._downy) > 5)) { + let bounds = cdiv.getBoundingClientRect(); + let pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY); + let separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY)); + let dragdist = Math.sqrt((pt[0] - this._downx) * (pt[0] - this._downx) + (pt[1] - this._downy) * (pt[1] - this._downy)) + if (separation > 100) { + DragLinksAsDocuments(this._ref.current!, pt[0], pt[1], this.props.ContainingCollectionDoc as Doc, this.props.Document); // Containging collection is the document, not a collection... hack. + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + } else if (dragdist > separation) { + this.props.Document[this.props.fieldKey + "_x"] = (pt[0] - bounds.left) / bounds.width * 100; + this.props.Document[this.props.fieldKey + "_y"] = (pt[1] - bounds.top) / bounds.height * 100; + } + } + }) + onPointerUp = (e: PointerEvent) => { + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button === 2 || e.ctrlKey)) { + this.props.select(false); + } + } + onClick = (e: React.MouseEvent) => { + if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button !== 2 && !e.ctrlKey)) { + DocumentManager.Instance.FollowLink(this.props.Document, this.props.Document[this.props.fieldKey] as Doc, document => this.props.addDocTab(document, undefined, "inTab"), false); + } + e.stopPropagation(); + } + render() { + let y = NumCast(this.props.Document[this.props.fieldKey + "_y"], 100); + let x = NumCast(this.props.Document[this.props.fieldKey + "_x"], 100); + let c = StrCast(this.props.Document.backgroundColor, "lightblue"); + return
    + } +} diff --git a/src/client/views/nodes/DocuLinkView.tsx b/src/client/views/nodes/DocuLinkView.tsx deleted file mode 100644 index 622d41047..000000000 --- a/src/client/views/nodes/DocuLinkView.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { action, observable } from "mobx"; -import { observer } from "mobx-react"; -import { Doc, Opt } from "../../../new_fields/Doc"; -import { NumCast, StrCast } from "../../../new_fields/Types"; -import { Utils } from '../../../Utils'; -import { DocumentManager } from "../../util/DocumentManager"; -import "./DocumentView.scss"; -import React = require("react"); -import { DragManager, DragLinksAsDocuments } from "../../util/DragManager"; -import { UndoManager } from "../../util/UndoManager"; - - -interface DocuLinkViewProps { - Document: Doc; - isSelected: () => boolean; - addDocTab: (doc: Doc, dataDoc: Opt, where: string) => void; - anchor: string; - otherAnchor: string; - scale: () => number; - contentDiv: HTMLDivElement | null; - link: Doc; - index: number; - selectedLink: () => number; - selectLink: (id: number) => void; - blacklist: Opt -} - -@observer -export class DocuLinkView extends React.Component { - _downx = 0; - _downy = 0; - @observable _x = 0; - @observable _y = 0; - @observable _selected = false; - _ref = React.createRef(); - - onPointerDown = (e: React.PointerEvent) => { - this._downx = e.clientX; - this._downy = e.clientY; - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointerup", this.onPointerUp); - e.stopPropagation(); - } - onPointerMove = action((e: PointerEvent) => { - if (this.props.contentDiv && (Math.abs(e.clientX - this._downx) > 5 || Math.abs(e.clientY - this._downy) > 5)) { - let bounds = this.props.contentDiv.getBoundingClientRect(); - let pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY); - let separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY)); - let dragdist = Math.sqrt((pt[0] - this._downx) * (pt[0] - this._downx) + (pt[1] - this._downy) * (pt[1] - this._downy)) - if (separation > 100) { - DragLinksAsDocuments(this._ref.current!, pt[0], pt[1], this.props.Document, this.props.link); - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - } else if (dragdist > separation) { - this.props.link[this.props.anchor + "_x"] = (pt[0] - bounds.left) / bounds.width * 100; - this.props.link[this.props.anchor + "_y"] = (pt[1] - bounds.top) / bounds.height * 100; - } - } - }) - onPointerUp = (e: PointerEvent) => { - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button === 2 || e.ctrlKey)) { - this.props.selectLink(this.props.selectedLink() === this.props.index ? -1 : this.props.index); - } - } - onClick = (e: React.MouseEvent) => { - if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button !== 2 && !e.ctrlKey)) { - DocumentManager.Instance.FollowLink(this.props.link, this.props.link[this.props.anchor] as Doc, document => this.props.addDocTab(document, undefined, "inTab"), false); - } - e.stopPropagation(); - } - render() { - let y = NumCast(this.props.link[this.props.anchor + "_y"], 100); - let x = NumCast(this.props.link[this.props.anchor + "_x"], 100); - let c = StrCast(this.props.link[this.props.anchor + "_background"], "lightblue"); - return
    - } -} diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 19ffdf0cd..4971e6ce5 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -26,6 +26,7 @@ import { PDFBox } from "./PDFBox"; import { PresBox } from "./PresBox"; import { QueryBox } from "./QueryBox"; import { ColorBox } from "./ColorBox"; +import { DocuLinkBox } from "./DocuLinkBox"; import { PresElementBox } from "../presentationview/PresElementBox"; import { VideoBox } from "./VideoBox"; import { WebBox } from "./WebBox"; @@ -104,7 +105,7 @@ export class DocumentContentsView extends React.Component number; animateBetweenIcon?: (maximize: boolean, target: number[]) => void; ChromeHeight?: () => number; + layoutKey?: string; } export const documentSchema = createSchema({ @@ -242,7 +242,6 @@ export class DocumentView extends DocComponent(Docu onPointerDown = (e: React.PointerEvent): void => { if (e.nativeEvent.cancelBubble && e.button === 0) return; - runInAction(() => this._selectedLink = -1); this._downX = e.clientX; this._downY = e.clientY; this._hitTemplateDrag = false; @@ -625,27 +624,10 @@ export class DocumentView extends DocComponent(Docu isSelected={this.isSelected} select={this.select} onClick={this.onClickHandler} - layoutKey="layout" + layoutKey={this.props.layoutKey || "layout"} DataDoc={this.props.DataDoc} />); } - linkEndpoint = (linkDoc: Doc) => Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? "anchor1" : "anchor2"; - linkOtherEndpoint = (linkDoc: Doc) => Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? "anchor2" : "anchor1"; - public setBackround(color: string) { - let selLink = this._selectedLink !== -1 && DocListCast(this.Document.links)[this._selectedLink]; - if (selLink) { - let both = selLink["anchor1_background"] === selLink["anchor2_background"]; - selLink[this.linkEndpoint(selLink) + "_background"] = color; - both && (selLink[this.linkOtherEndpoint(selLink) + "_background"] = color); - } else { - this.Document.backgroundColor = color; - } - } - @observable _selectedLink = -1; - selectLink = action((which: number) => { - SelectionManager.SelectDoc(this, false); - this._selectedLink = which; - }) - selectedLink = () => this._selectedLink; + linkEndpoint = (linkDoc: Doc) => Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? "layoutKey1" : "layoutKey2"; render() { if (!this.props.Document) return (null); @@ -717,10 +699,7 @@ export class DocumentView extends DocComponent(Docu onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} onPointerEnter={() => Doc.BrushDoc(this.props.Document)} onPointerLeave={() => Doc.UnBrushDoc(this.props.Document)} > - {this.props.Document.links && DocListCast(this.props.Document.links).map((d, i) => - )} + {this.props.Document.links && DocListCast(this.props.Document.links).map((d, i) => )} {!showTitle && !showCaption ? this.Document.searchFields ? (
    -- cgit v1.2.3-70-g09d2 From 73cb8628d13cc71e58717e97ca073c8b3316dd63 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 17 Oct 2019 23:40:29 -0400 Subject: working version of link lines between anchors --- src/client/documents/Documents.ts | 1 + src/client/util/DocumentManager.ts | 7 ++- src/client/util/LinkManager.ts | 6 ++- src/client/views/OverlayView.tsx | 2 + .../CollectionFreeFormLinkView.scss | 3 -- .../CollectionFreeFormLinkView.tsx | 63 ++++++---------------- .../CollectionFreeFormLinksView.scss | 5 +- .../CollectionFreeFormLinksView.tsx | 44 +++++---------- .../collectionFreeForm/CollectionFreeFormView.tsx | 8 ++- src/client/views/nodes/DocuLinkBox.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/new_fields/Doc.ts | 2 +- 12 files changed, 50 insertions(+), 97 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index edc2ac653..d979e0a3f 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -724,6 +724,7 @@ export namespace DocUtils { linkDocProto.layoutKey2 = DocuLinkBox.LayoutString("anchor2"); linkDocProto.width = linkDocProto.height = 0; linkDocProto.isBackground = true; + linkDocProto.isButton = true; LinkManager.Instance.addLink(linkDocProto); diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index e23ac55c2..fc406c6ab 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -9,6 +9,7 @@ import { DocumentView } from '../views/nodes/DocumentView'; import { LinkManager } from './LinkManager'; import { Scripting } from './Scripting'; import { SelectionManager } from './SelectionManager'; +import { DocumentType } from '../documents/DocumentTypes'; export class DocumentManager { @@ -110,14 +111,18 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { + console.log("START"); let pairs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || Doc.IsBrushed(dv.props.Document)).reduce((pairs, dv) => { + console.log("DOC = " + dv.props.Document.title + " " + dv.props.Document.layout); let linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document); pairs.push(...linksList.reduce((pairs, link) => { if (link) { let linkToDoc = LinkManager.Instance.getOppositeAnchor(link, dv.props.Document); if (linkToDoc) { DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => { - pairs.push({ a: dv, b: docView1, l: link }); + if (dv.props.Document.type !== DocumentType.LINK || dv.props.layoutKey !== docView1.props.layoutKey) { + pairs.push({ a: dv, b: docView1, l: link }); + } }); } } diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 8a668e8d8..35c0f023f 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -79,7 +79,7 @@ export class LinkManager { let related = LinkManager.Instance.getAllLinks().filter(link => { let protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, null)); let protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, null)); - return protomatch1 || protomatch2; + return protomatch1 || protomatch2 || Doc.AreProtosEqual(link, anchor); }); return related; } @@ -244,8 +244,10 @@ export class LinkManager { public getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc | undefined { if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, null))) { return Cast(linkDoc.anchor2, Doc, null); - } else { + } else if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor2, Doc, null))) { return Cast(linkDoc.anchor1, Doc, null); + } else if (Doc.AreProtosEqual(anchor, linkDoc)) { + return linkDoc; } } } diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 95c7b2683..9869e24d1 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -12,6 +12,7 @@ import { Transform } from "../util/Transform"; import { CollectionFreeFormDocumentView } from "./nodes/CollectionFreeFormDocumentView"; import { DocumentContentsView } from "./nodes/DocumentContentsView"; import { NumCast } from "../../new_fields/Types"; +import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView"; export type OverlayDisposer = () => void; @@ -206,6 +207,7 @@ export class OverlayView extends React.Component {
    {this._elements}
    + {this.overlayDocs}
    ); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss index cfd18ad35..1f1bca2f2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss @@ -1,6 +1,5 @@ .collectionfreeformlinkview-linkLine { stroke: black; - transform: translate(10000px,10000px); opacity: 0.8; pointer-events: all; stroke-width: 3px; @@ -8,13 +7,11 @@ .collectionfreeformlinkview-linkCircle { stroke: rgb(0,0,0); opacity: 0.5; - transform: translate(10000px,10000px); pointer-events: all; cursor: pointer; } .collectionfreeformlinkview-linkText { stroke: rgb(0,0,0); opacity: 0.5; - transform: translate(10000px,10000px); pointer-events: all; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index a59fda6d9..4e1faccd7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -1,63 +1,30 @@ import { observer } from "mobx-react"; -import { Doc, HeightSym, WidthSym } from "../../../../new_fields/Doc"; -import { BoolCast, NumCast, StrCast } from "../../../../new_fields/Types"; -import { InkingControl } from "../../InkingControl"; +import { Doc } from "../../../../new_fields/Doc"; +import { Utils } from '../../../../Utils'; +import { DocumentView } from "../../nodes/DocumentView"; import "./CollectionFreeFormLinkView.scss"; import React = require("react"); import v5 = require("uuid/v5"); +import { DocumentType } from "../../../documents/DocumentTypes"; export interface CollectionFreeFormLinkViewProps { - A: Doc; - B: Doc; + A: DocumentView; + B: DocumentView; LinkDocs: Doc[]; - addDocument: (document: Doc) => boolean; - removeDocument: (document: Doc) => boolean; } @observer export class CollectionFreeFormLinkView extends React.Component { - onPointerDown = (e: React.PointerEvent) => { - if (e.button === 0 && !InkingControl.Instance.selectedTool) { - let a = this.props.A; - let b = this.props.B; - let x1 = NumCast(a.x) + (BoolCast(a.isMinimized) ? 5 : a[WidthSym]() / 2); - let y1 = NumCast(a.y) + (BoolCast(a.isMinimized) ? 5 : a[HeightSym]() / 2); - let x2 = NumCast(b.x) + (BoolCast(b.isMinimized) ? 5 : b[WidthSym]() / 2); - let y2 = NumCast(b.y) + (BoolCast(b.isMinimized) ? 5 : b[HeightSym]() / 2); - // this.props.LinkDocs.map(l => { - // let width = l[WidthSym](); - // l.x = (x1 + x2) / 2 - width / 2; - // l.y = (y1 + y2) / 2 + 10; - // if (!this.props.removeDocument(l)) this.props.addDocument(l, false); - // }); - e.stopPropagation(); - e.preventDefault(); - } - } render() { - // let l = this.props.LinkDocs; - let a = this.props.A.layout instanceof Doc ? this.props.A.layout : this.props.A; - let b = this.props.B.layout instanceof Doc ? this.props.B.layout : this.props.B; - let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.width) / 2); - let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.height) / 2); - let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.width) / 2); - let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.height) / 2); - let text = ""; - // let first = this.props.LinkDocs[0]; - // if (this.props.LinkDocs.length === 1) text += first.title + (first.linkDescription ? "(" + StrCast(first.linkDescription) + ")" : ""); - // else text = "-multiple-"; - return ( - <> - - {/* */} - - {text} - - - ); + let acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : []; + let bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : []; + let a = (acont.length ? acont[0] : this.props.A.ContentDiv!).getBoundingClientRect(); + let b = (bcont.length ? bcont[0] : this.props.B.ContentDiv!).getBoundingClientRect(); + let pt1 = Utils.getNearestPointInPerimeter(a.left, a.top, a.width, a.height, b.left + b.width / 2, b.top + b.height / 2); + let pt2 = Utils.getNearestPointInPerimeter(b.left, b.top, b.width, b.height, a.left + a.width / 2, a.top + a.height / 2); + return (); } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss index 30e158603..cb5cef29c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss @@ -1,10 +1,9 @@ .collectionfreeformlinksview-svgCanvas{ - transform: translate(-10000px,-10000px); position: absolute; top: 0; left: 0; - width: 20000px; - height: 20000px; + width: 100%; + height: 100%; pointer-events: none; } .collectionfreeformlinksview-container { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 189981e35..cf0e55ccf 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -1,17 +1,17 @@ import { computed, IReactionDisposer } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast } from "../../../../new_fields/Doc"; +import { Doc } from "../../../../new_fields/Doc"; import { Id } from "../../../../new_fields/FieldSymbols"; -import { Cast, FieldValue } from "../../../../new_fields/Types"; import { DocumentManager } from "../../../util/DocumentManager"; import { DocumentView } from "../../nodes/DocumentView"; -import { CollectionViewProps } from "../CollectionSubView"; import "./CollectionFreeFormLinksView.scss"; import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView"; import React = require("react"); +import { Utils } from "../../../../Utils"; +import { SelectionManager } from "../../../util/SelectionManager"; @observer -export class CollectionFreeFormLinksView extends React.Component { +export class CollectionFreeFormLinksView extends React.Component { _brushReactionDisposer?: IReactionDisposer; componentDidMount() { @@ -71,35 +71,18 @@ export class CollectionFreeFormLinksView extends React.Component - child[Id] === collid).map(view => - DocumentManager.Instance.getDocumentViews(view).map(view => - equalViews.push(view))); - } - return equalViews.filter(sv => sv.props.ContainingCollectionDoc === this.props.Document); - } - @computed get uniqueConnections() { let connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => { - let srcViews = this.documentAnchors(connection.a); - let targetViews = this.documentAnchors(connection.b); + let srcViews = [connection.a]; + let targetViews = [connection.b]; - let possiblePairs: { a: Doc, b: Doc, }[] = []; - srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv.props.Document, b: tv.props.Document }))); + let possiblePairs: { a: DocumentView, b: DocumentView, }[] = []; + srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv, b: tv }))); possiblePairs.map(possiblePair => { if (!drawnPairs.reduce((found, drawnPair) => { - let match1 = (Doc.AreProtosEqual(possiblePair.a, drawnPair.a) && Doc.AreProtosEqual(possiblePair.b, drawnPair.b)); - let match2 = (Doc.AreProtosEqual(possiblePair.a, drawnPair.b) && Doc.AreProtosEqual(possiblePair.b, drawnPair.a)); + let match1 = (Doc.AreProtosEqual(possiblePair.a.props.Document, drawnPair.a.props.Document) && Doc.AreProtosEqual(possiblePair.b.props.Document, drawnPair.b.props.Document)); + let match2 = (Doc.AreProtosEqual(possiblePair.a.props.Document, drawnPair.b.props.Document) && Doc.AreProtosEqual(possiblePair.b.props.Document, drawnPair.a.props.Document)); let match = match1 || match2; if (match && !drawnPair.l.reduce((found, link) => found || link[Id] === connection.l[Id], false)) { drawnPair.l.push(connection.l); @@ -110,16 +93,15 @@ export class CollectionFreeFormLinksView extends React.Component p + l[Id], "")} A={c.a} B={c.b} LinkDocs={c.l} - removeDocument={this.props.removeDocument} addDocument={this.props.addDocument} />); + }, [] as { a: DocumentView, b: DocumentView, l: Doc[] }[]); + return connections.map(c => ); } render() { return (
    - {this.uniqueConnections} + {SelectionManager.GetIsDragging() ? (null) : this.uniqueConnections} {this.props.children}
    diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 5dccb5bf0..b3270d507 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -705,11 +705,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}> - - - {this.childViews} - - + + {this.childViews} + diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx index 8c41b9d4f..f56bf4ad3 100644 --- a/src/client/views/nodes/DocuLinkBox.tsx +++ b/src/client/views/nodes/DocuLinkBox.tsx @@ -54,12 +54,12 @@ export class DocuLinkBox extends DocComponent(Doc onPointerUp = (e: PointerEvent) => { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); - if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button === 2 || e.ctrlKey)) { + if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button === 2 || e.ctrlKey || !this.props.Document.isButton)) { this.props.select(false); } } onClick = (e: React.MouseEvent) => { - if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button !== 2 && !e.ctrlKey)) { + if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button !== 2 && !e.ctrlKey && this.props.Document.isButton)) { DocumentManager.Instance.FollowLink(this.props.Document, this.props.Document[this.props.fieldKey] as Doc, document => this.props.addDocTab(document, undefined, "inTab"), false); } e.stopPropagation(); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6775655eb..0bcaa99aa 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -697,7 +697,7 @@ export class DocumentView extends DocComponent(Docu opacity: this.Document.opacity }} onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} - onPointerEnter={() => Doc.BrushDoc(this.props.Document)} onPointerLeave={() => Doc.UnBrushDoc(this.props.Document)} + onPointerEnter={() => { Doc.UnBrushAllDocs(); Doc.BrushDoc(this.props.Document); }} onPointerLeave={() => Doc.UnBrushDoc(this.props.Document)} > {this.props.Document.links && DocListCast(this.props.Document.links).map((d, i) =>
    )} diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 04f310785..26355d0e7 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -734,7 +734,7 @@ export namespace Doc { } export function UnBrushAllDocs() { - manager.BrushedDoc.clear(); + brushManager.BrushedDoc.clear(); } export function setChildLayout(target: Doc, source?: Doc) { -- cgit v1.2.3-70-g09d2 From c9a84a7d74efa820d1dc55d8ef93bec40315be4a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 18 Oct 2019 01:29:33 -0400 Subject: link lines are working properly except when things get panned/moved ... need to turn off or get updates somehow. --- src/client/documents/Documents.ts | 15 ++-------- src/client/util/DocumentManager.ts | 11 +++++-- src/client/views/DocumentDecorations.tsx | 3 +- .../CollectionFreeFormLinksView.tsx | 4 +-- .../collectionFreeForm/CollectionFreeFormView.tsx | 35 ---------------------- src/client/views/nodes/DocumentContentsView.tsx | 32 ++++++++------------ src/client/views/nodes/DocumentView.tsx | 9 ++---- src/new_fields/Doc.ts | 6 ---- 8 files changed, 28 insertions(+), 87 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d979e0a3f..b53549f7a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -80,7 +80,6 @@ export interface DocumentOptions { opacity?: number; defaultBackgroundColor?: string; dropAction?: dropActionType; - backgroundLayout?: string; chromeStatus?: string; columnWidth?: number; fontSize?: number; @@ -127,7 +126,6 @@ export namespace Docs { layout: { view: LayoutSource, ext?: string, // optional extension field for layout source - collectionView?: CollectionViewType }, options?: Partial }; @@ -142,7 +140,7 @@ export namespace Docs { options: { height: 150, backgroundColor: "#f1efeb", defaultBackgroundColor: "#f1efeb" } }], [DocumentType.HIST, { - layout: { view: HistogramBox, collectionView: [CollectionView, data] as CollectionViewType }, + layout: { view: HistogramBox }, options: { height: 300, backgroundColor: "black" } }], [DocumentType.QUERY, { @@ -290,15 +288,8 @@ export namespace Docs { // synthesize the default options, the type and title from computed values and // whatever options pertain to this specific prototype let options = { title, type, baseProto: true, ...defaultOptions, ...(template.options || {}) }; - let primary = layout.view.LayoutString(layout.ext); - let collectionView = layout.collectionView; - if (collectionView) { - options.layout = collectionView[0].LayoutString(collectionView[1], collectionView[2]); - options.backgroundLayout = primary; - } else { - options.layout = primary; - } - return Doc.assign(new Doc(prototypeId, true), { ...options, baseLayout: primary }); + options.layout = layout.view.LayoutString(layout.ext); + return Doc.assign(new Doc(prototypeId, true), { ...options, baseLayout: options.layout }); } } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index aed7aafd1..ee6772f8f 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,5 +1,5 @@ import { action, computed, observable } from 'mobx'; -import { Doc, DocListCastAsync } from '../../new_fields/Doc'; +import { Doc, DocListCastAsync, DocListCast } from '../../new_fields/Doc'; import { Id } from '../../new_fields/FieldSymbols'; import { List } from '../../new_fields/List'; import { Cast, NumCast, StrCast } from '../../new_fields/Types'; @@ -100,7 +100,13 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { - let pairs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || Doc.IsBrushed(dv.props.Document)).reduce((pairs, dv) => { + let pairs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || Doc.IsBrushed(dv.props.Document) + || DocumentManager.Instance.DocumentViews.some(dv2 => { + let init = dv2.isSelected() || Doc.IsBrushed(dv2.props.Document); + let rest = DocListCast(dv2.props.Document.links).some(l => Doc.AreProtosEqual(l, dv.props.Document)); + return init && rest; + }) + ).reduce((pairs, dv) => { let linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document); pairs.push(...linksList.reduce((pairs, link) => { let linkToDoc = link && LinkManager.Instance.getOppositeAnchor(link, dv.props.Document); @@ -111,7 +117,6 @@ export class DocumentManager { }); return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); - // } return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 6e8ba2d3d..8409a34da 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -111,7 +111,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> Doc.GetProto(docTemplate).layoutNative = layoutNative; swapViews(fieldTemplate, "", "layoutNative", layoutNative); layoutNative.layout = StrCast(fieldTemplateView.props.Document.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); - layoutNative.backgroundLayout = StrCast(fieldTemplateView.props.Document.backgroundLayout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); } } } @@ -343,7 +342,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let iconDoc: Doc | undefined = await Cast(doc.minimizedDoc, Doc); if (!iconDoc || !DocumentManager.Instance.getDocumentView(iconDoc)) { - const layout = StrCast(doc.backgroundLayout, StrCast(doc.layout, FieldView.LayoutString(DocumentView))); + const layout = StrCast(doc.layout, FieldView.LayoutString(DocumentView)); iconDoc = this.createIcon([docView], layout); } return iconDoc; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index a26febda4..e9191c176 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -68,9 +68,7 @@ export class CollectionFreeFormLinksView extends React.Component { // }); } componentWillUnmount() { - if (this._brushReactionDisposer) { - this._brushReactionDisposer(); - } + this._brushReactionDisposer && this._brushReactionDisposer(); } @computed get uniqueConnections() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b3270d507..932b6722b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -467,22 +467,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { getScale: this.getScale }; } - getDocumentViewProps(layoutDoc: Doc): DocumentViewProps { - return { - ...this.props, - ScreenToLocalTransform: this.getTransform, - PanelWidth: layoutDoc[WidthSym], - PanelHeight: layoutDoc[HeightSym], - ContentScaling: returnOne, - ContainingCollectionView: this.props.ContainingCollectionView, - focus: this.focusDocument, - backgroundColor: returnEmptyString, - parentActive: this.props.active, - bringToFront: this.bringToFront, - zoomToScale: this.zoomToScale, - getScale: this.getScale - }; - } getCalculatedPositions(params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): { x?: number, y?: number, z?: number, width?: number, height?: number, transition?: string, state?: any } { const script = this.Document.arrangeScript; @@ -682,7 +666,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private childViews = () => { let children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : []; return [ - , ...children, ...this.views, ]; @@ -712,29 +695,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { {this.overlayViews} -
    ); } } -@observer -class CollectionFreeFormOverlayView extends React.Component boolean }> { - render() { - return ; - } -} - -@observer -class CollectionFreeFormBackgroundView extends React.Component boolean }> { - render() { - return !this.props.Document.backgroundLayout ? (null) : - (); - } -} - interface CollectionFreeFormViewPannableContentsProps { centeringShiftX: () => number; centeringShiftY: () => number; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 4971e6ce5..d375405b9 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -92,26 +92,20 @@ export class DocumentContentsView extends React.Component" : this.layout; - } - render() { - let self = this; - if (this.props.renderDepth > 7) return (null); - if (!this.layout && this.props.layoutKey !== "overlayLayout") return (null); - return 7 || !this.layout) ? (null) : + { console.log(test); }} - />; + onError={(test: any) => { console.log(test); }} + />; } } \ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d4c2c512c..48ad7a632 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -163,6 +163,7 @@ export class DocumentView extends DocComponent(Docu @action componentWillUnmount() { this._dropDisposer && this._dropDisposer(); + Doc.UnBrushDoc(this.props.Document); DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1); } @@ -697,11 +698,7 @@ export class DocumentView extends DocComponent(Docu opacity: this.Document.opacity }} onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} - onPointerEnter={() => { - Doc.UnBrushAllDocs(); - DocListCast(this.props.Document.links).map(Doc.BrushDoc); - Doc.BrushDoc(this.props.Document); - }} onPointerLeave={() => Doc.UnBrushDoc(this.props.Document)} + onPointerEnter={() => Doc.BrushDoc(this.props.Document)} onPointerLeave={() => Doc.UnBrushDoc(this.props.Document)} > {this.props.Document.links && DocListCast(this.props.Document.links).map((d, i) =>
    @@ -739,7 +736,6 @@ export async function swapViews(doc: Doc, newLayoutField: string, oldLayoutField oldLayoutExt.nativeWidth = doc.nativeWidth; oldLayoutExt.nativeHeight = doc.nativeHeight; oldLayoutExt.ignoreAspect = doc.ignoreAspect; - oldLayoutExt.backgroundLayout = doc.backgroundLayout; oldLayoutExt.type = doc.type; oldLayoutExt.layout = doc.layout; } @@ -752,7 +748,6 @@ export async function swapViews(doc: Doc, newLayoutField: string, oldLayoutField doc.nativeWidth = newLayoutExt.nativeWidth; doc.nativeHeight = newLayoutExt.nativeHeight; doc.ignoreAspect = newLayoutExt.ignoreAspect; - doc.backgroundLayout = newLayoutExt.backgroundLayout; doc.type = newLayoutExt.type; doc.layout = await newLayoutExt.layout; } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 26355d0e7..69c048ebf 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -592,7 +592,6 @@ export namespace Doc { target.ignoreAspect = templateDoc.nativeWidth ? true : false; target.onClick = templateDoc.onClick instanceof ObjectField && templateDoc.onClick[Copy](); target.layout = layoutCustomLayout; - target.backgroundLayout = layoutCustomLayout.backgroundLayout; target.layoutNative = Cast(templateDoc.layoutNative, Doc) as Doc; target.layoutCustom = layoutCustom; @@ -602,19 +601,14 @@ export namespace Doc { export function MakeMetadataFieldTemplate(fieldTemplate: Doc, templateDataDoc: Doc, suppressTitle: boolean = false): boolean { // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??) let metadataFieldName = StrCast(fieldTemplate.title).replace(/^-/, ""); - let backgroundLayout = StrCast(fieldTemplate.backgroundLayout); let fieldLayoutDoc = fieldTemplate; if (fieldTemplate.layout instanceof Doc) { fieldLayoutDoc = Doc.MakeDelegate(fieldTemplate.layout); } - if (backgroundLayout) { - backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metadataFieldName}"}`); - } fieldTemplate.templateField = metadataFieldName; fieldTemplate.title = metadataFieldName; fieldTemplate.isTemplateField = true; - fieldTemplate.backgroundLayout = backgroundLayout; /* move certain layout properties from the original data doc to the template layout to avoid inheriting them from the template's data doc which may also define these fields for its own use. */ -- cgit v1.2.3-70-g09d2 From 11537da75c76fba79a2709d2ad175dfa16a25256 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 18 Oct 2019 14:00:25 -0400 Subject: fixes for drawing link anchors to pdf text selections. --- src/client/documents/DocumentTypes.ts | 3 ++- src/client/util/DocumentManager.ts | 9 +++++---- src/client/util/LinkManager.ts | 19 +++++++------------ src/client/views/MainView.tsx | 3 ++- src/client/views/nodes/DocuLinkBox.tsx | 7 +++++-- src/client/views/nodes/DocumentView.scss | 3 --- src/client/views/nodes/DocumentView.tsx | 2 ++ src/client/views/pdf/Annotation.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 6 ++++++ 9 files changed, 30 insertions(+), 24 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index ea37fc2f1..12501065a 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -23,5 +23,6 @@ export enum DocumentType { PRESELEMENT = "preselement", QUERY = "search", COLOR = "color", - DOCULINK = "doculink" + DOCULINK = "doculink", + PDFANNO = "pdfanno" } \ No newline at end of file diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index ee6772f8f..0f2a47dd0 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -100,10 +100,11 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { - let pairs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || Doc.IsBrushed(dv.props.Document) - || DocumentManager.Instance.DocumentViews.some(dv2 => { - let init = dv2.isSelected() || Doc.IsBrushed(dv2.props.Document); - let rest = DocListCast(dv2.props.Document.links).some(l => Doc.AreProtosEqual(l, dv.props.Document)); + let pairs = DocumentManager.Instance.DocumentViews.filter(dv => + dv.isSelected() || Doc.IsBrushed(dv.props.Document) // draw links from DocumentViews that are selected or brushed OR + || DocumentManager.Instance.DocumentViews.some(dv2 => { // Documentviews which + let rest = DocListCast(dv2.props.Document.links).some(l => Doc.AreProtosEqual(l, dv.props.Document));// are link doc anchors + let init = dv2.isSelected() || Doc.IsBrushed(dv2.props.Document); // on a view that is selected or brushed return init && rest; }) ).reduce((pairs, dv) => { diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 35c0f023f..ee2f2dadc 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -1,10 +1,7 @@ -import { observable, action } from "mobx"; -import { StrCast, Cast, FieldValue } from "../../new_fields/Types"; import { Doc, DocListCast } from "../../new_fields/Doc"; -import { listSpec } from "../../new_fields/Schema"; import { List } from "../../new_fields/List"; -import { Id } from "../../new_fields/FieldSymbols"; -import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; +import { listSpec } from "../../new_fields/Schema"; +import { Cast, StrCast } from "../../new_fields/Types"; import { Docs } from "../documents/Documents"; import { Scripting } from "./Scripting"; @@ -242,13 +239,11 @@ export class LinkManager { //TODO This should probably return undefined if there isn't an opposite anchor //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 | undefined { - if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, null))) { - return Cast(linkDoc.anchor2, Doc, null); - } else if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor2, Doc, null))) { - return Cast(linkDoc.anchor1, Doc, null); - } else if (Doc.AreProtosEqual(anchor, linkDoc)) { - return linkDoc; - } + let a1 = Cast(linkDoc.anchor1, Doc, null); + let a2 = Cast(linkDoc.anchor2, Doc, null); + if (Doc.AreProtosEqual(anchor, a1)) return a2; + if (Doc.AreProtosEqual(anchor, a2)) return a1; + if (Doc.AreProtosEqual(anchor, linkDoc)) return linkDoc; } } Scripting.addGlobal(function links(doc: any) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 9304f4bef..4c2b6f262 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -183,7 +183,8 @@ export class MainView extends React.Component { y: 400, width: this._panelWidth * .7, height: this._panelHeight, - title: "My Blank Collection" + title: "My Blank Collection", + backgroundColor: "white" }; let workspaces: FieldResult; let freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx index f56bf4ad3..3294a5aa2 100644 --- a/src/client/views/nodes/DocuLinkBox.tsx +++ b/src/client/views/nodes/DocuLinkBox.tsx @@ -2,7 +2,7 @@ import { action, observable } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../../new_fields/Doc"; import { makeInterface } from "../../../new_fields/Schema"; -import { NumCast, StrCast } from "../../../new_fields/Types"; +import { NumCast, StrCast, Cast } from "../../../new_fields/Types"; import { Utils } from '../../../Utils'; import { DocumentManager } from "../../util/DocumentManager"; import { DragLinksAsDocuments } from "../../util/DragManager"; @@ -11,6 +11,7 @@ import { documentSchema } from "./DocumentView"; import "./DocuLinkBox.scss"; import { FieldView, FieldViewProps } from "./FieldView"; import React = require("react"); +import { DocumentType } from "../../documents/DocumentTypes"; type DocLinkSchema = makeInterface<[typeof documentSchema]>; const DocLinkDocument = makeInterface(documentSchema); @@ -65,13 +66,15 @@ export class DocuLinkBox extends DocComponent(Doc e.stopPropagation(); } render() { + let anchorDoc = Cast(this.props.Document[this.props.fieldKey], Doc); + let hasAnchor = anchorDoc instanceof Doc && anchorDoc.type === DocumentType.PDFANNO; let y = NumCast(this.props.Document[this.props.fieldKey + "_y"], 100); let x = NumCast(this.props.Document[this.props.fieldKey + "_x"], 100); let c = StrCast(this.props.Document.backgroundColor, "lightblue"); return
    ; } } diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 69eb1a843..a0bf74990 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -47,9 +47,6 @@ } } } -.documentView-node-topmost { - background: white; -} .documentView-styleWrapper { position: absolute; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 48ad7a632..8bf698391 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -629,6 +629,7 @@ export class DocumentView extends DocComponent(Docu DataDoc={this.props.DataDoc} />); } linkEndpoint = (linkDoc: Doc) => Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? "layoutKey1" : "layoutKey2"; + linkEndpointDoc = (linkDoc: Doc) => Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? Cast(linkDoc.anchor1, Doc) as Doc : Cast(linkDoc.anchor2, Doc) as Doc; render() { if (!this.props.Document) return (null); @@ -701,6 +702,7 @@ export class DocumentView extends DocComponent(Docu onPointerEnter={() => Doc.BrushDoc(this.props.Document)} onPointerLeave={() => Doc.UnBrushDoc(this.props.Document)} > {this.props.Document.links && DocListCast(this.props.Document.links).map((d, i) => + //this.linkEndpointDoc(d).type === DocumentType.PDFANNO ? (null) :
    )} diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index ad6240c70..e0a3b9171 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -100,7 +100,7 @@ class RegionAnnotation extends React.Component { let annoGroup = await Cast(this.props.document.group, Doc); if (annoGroup) { DocumentManager.Instance.FollowLink(undefined, annoGroup, - (doc: Doc, maxLocation: string) => this.props.addDocTab(doc, undefined, e.ctrlKey ? "onRight" : "inTab"), + (doc: Doc, maxLocation: string) => this.props.addDocTab(doc, undefined, e.ctrlKey ? "inTab" : "onRight"), false, false, undefined); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 1bae6128c..6e5f1a981 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -25,6 +25,7 @@ import { SelectionManager } from "../../util/SelectionManager"; import { undoBatch } from "../../util/UndoManager"; import { DocAnnotatableComponent } from "../DocComponent"; import { documentSchema } from "../nodes/DocumentView"; +import { DocumentType } from "../../documents/DocumentTypes"; const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); const pdfjsLib = require("pdfjs-dist"); @@ -249,6 +250,7 @@ export class PDFViewer extends DocAnnotatableComponent(annoDocs); } mainAnnoDocProto.title = "Annotation on " + StrCast(this.props.Document.title); -- cgit v1.2.3-70-g09d2 From d18ed8aa5a06efff1d7a3db1c2c8240dfa3c393c Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 18 Oct 2019 15:24:28 -0400 Subject: fixed border rounding of floating icon buttons --- src/client/documents/Documents.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b53549f7a..651662ded 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -203,7 +203,7 @@ export namespace Docs { }], [DocumentType.FONTICON, { layout: { view: FontIconBox }, - options: { width: 40, height: 40 }, + options: { width: 40, height: 40, borderRounding: "100%" }, }], [DocumentType.LINKFOLLOW, { layout: { view: LinkFollowBox } -- cgit v1.2.3-70-g09d2 From 1f3576a69a6a0396d07e965c700bb7d69d77a0a3 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 18 Oct 2019 17:06:41 -0400 Subject: fixed up creator buttons - factored out convert to buttons code into a dropConverter script. fixed issues with template loading for images. --- src/client/documents/Documents.ts | 2 +- src/client/util/DragManager.ts | 3 ++ src/client/util/DropConverter.ts | 46 ++++++++++++++++++++++ src/client/views/CollectionLinearView.tsx | 40 ------------------- .../views/collections/CollectionStackingView.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 6 ++- .../views/collections/CollectionTreeView.scss | 1 - .../views/collections/CollectionTreeView.tsx | 2 +- .../CollectionFreeFormLinkView.tsx | 2 +- .../authentication/models/current_user_utils.ts | 11 ++++-- 10 files changed, 65 insertions(+), 50 deletions(-) create mode 100644 src/client/util/DropConverter.ts (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 651662ded..32fda1954 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -105,7 +105,7 @@ export interface DocumentOptions { yMargin?: number; // gap between top edge of dcoument and start of masonry/stacking layouts panel?: Doc; // panel to display in 'targetContainer' as the result of a button onClick script targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script - convertToButtons?: boolean; // whether documents dropped onto a collection should be converted to buttons that will construct the dropped document + dropConverter?: ScriptField; // script to run when documents are dropped on this Document. // [key: string]: Opt; } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 576b16bc8..bbc29585c 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -14,6 +14,8 @@ import { ScriptField } from "../../new_fields/ScriptField"; import { List } from "../../new_fields/List"; import { PrefetchProxy } from "../../new_fields/Proxy"; import { listSpec } from "../../new_fields/Schema"; +import { Scripting } from "./Scripting"; +import { convertDropDataToButtons } from "./DropConverter"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag( @@ -497,3 +499,4 @@ export namespace DragManager { } } } +Scripting.addGlobal(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); }); diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts new file mode 100644 index 000000000..eea3da1bc --- /dev/null +++ b/src/client/util/DropConverter.ts @@ -0,0 +1,46 @@ +import { DragManager } from "./DragManager"; +import { CollectionViewType } from "../views/collections/CollectionBaseView"; +import { Doc, DocListCast } from "../../new_fields/Doc"; +import { DocumentType } from "../documents/DocumentTypes"; +import { ObjectField } from "../../new_fields/ObjectField"; +import { StrCast } from "../../new_fields/Types"; +import { Docs } from "../documents/Documents"; +import { ScriptField } from "../../new_fields/ScriptField"; + + +function makeTemplate(doc: Doc): boolean { + let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc; + let layout = StrCast(layoutDoc.layout).match(/fieldKey={"[^"]*"}/)![0]; + let fieldKey = layout.replace('fieldKey={"', "").replace(/"}$/, ""); + let docs = DocListCast(layoutDoc[fieldKey]); + let any = false; + docs.map(d => { + if (!StrCast(d.title).startsWith("-")) { + any = true; + return Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc)); + } + if (d.type === DocumentType.COL) return makeTemplate(d); + return false; + }); + return any; +} +export function convertDropDataToButtons(data: DragManager.DocumentDragData) { + data && data.draggedDocuments.map((doc, i) => { + let dbox = doc; + if (!doc.onDragStart && !doc.onClick && doc.viewType !== CollectionViewType.Linear) { + let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc; + if (layoutDoc.type === DocumentType.COL) { + layoutDoc.isTemplateDoc = makeTemplate(layoutDoc); + } else { + layoutDoc.isTemplateDoc = (layoutDoc.type === DocumentType.TEXT || layoutDoc.layout instanceof Doc) && !data.userDropAction; + } + dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: layoutDoc.isTemplateDoc ? "font" : "bolt" }); + dbox.dragFactory = layoutDoc; + dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined; + dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); + } else if (doc.viewType === CollectionViewType.Linear) { + dbox.ignoreClick = true; + } + data.droppedDocuments[i] = dbox; + }); +} diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx index 04e131135..e8ef20899 100644 --- a/src/client/views/CollectionLinearView.tsx +++ b/src/client/views/CollectionLinearView.tsx @@ -47,46 +47,6 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { } } - makeTemplate = (doc: Doc): boolean => { - let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc; - let layout = StrCast(layoutDoc.layout).match(/fieldKey={"[^"]*"}/)![0]; - let fieldKey = layout.replace('fieldKey={"', "").replace(/"}$/, ""); - let docs = DocListCast(layoutDoc[fieldKey]); - let any = false; - docs.map(d => { - if (!StrCast(d.title).startsWith("-")) { - any = true; - return Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc)); - } - if (d.type === DocumentType.COL) return this.makeTemplate(d); - return false; - }); - return any; - } - - drop = action((e: Event, de: DragManager.DropEvent) => { - (de.data as DragManager.DocumentDragData).draggedDocuments.map((doc, i) => { - let dbox = doc; - if (!doc.onDragStart && !doc.onClick && this.props.Document.convertToButtons && doc.viewType !== CollectionViewType.Linear) { - let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc; - if (layoutDoc.type === DocumentType.COL) { - layoutDoc.isTemplateDoc = this.makeTemplate(layoutDoc); - } else { - layoutDoc.isTemplateDoc = (layoutDoc.type === DocumentType.TEXT || layoutDoc.layout instanceof Doc) && de.data instanceof DragManager.DocumentDragData && !de.data.userDropAction; - } - dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: layoutDoc.isTemplateDoc ? "font" : "bolt" }); - dbox.dragFactory = layoutDoc; - dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined; - dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); - } else if (doc.viewType === CollectionViewType.Linear) { - dbox.ignoreClick = true; - } - (de.data as DragManager.DocumentDragData).droppedDocuments[i] = dbox; - }); - e.stopPropagation(); - return super.drop(e, de); - }); - public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); } dimension = () => NumCast(this.props.Document.height); // 2 * the padding diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index e54374ad7..f9f040c6b 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -65,7 +65,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap); let style = this.isStackingView ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` }; return
    - {this.getDisplayDoc(pair.layout as Doc, pair.data, dxf, width)} + {pair.layout instanceof Doc && this.getDisplayDoc(pair.layout, pair.data, dxf, width)}
    ; }); } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 1f8c0b18f..bc61492d0 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -6,7 +6,7 @@ import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; import { ScriptField } from "../../../new_fields/ScriptField"; -import { Cast } from "../../../new_fields/Types"; +import { Cast, StrCast } from "../../../new_fields/Types"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { RouteStore } from "../../../server/RouteStore"; import { Utils } from "../../../Utils"; @@ -23,6 +23,8 @@ import React = require("react"); var path = require('path'); import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; +import { CollectionViewType } from "./CollectionBaseView"; +import { ObjectField } from "../../../new_fields/ObjectField"; export interface CollectionViewProps extends FieldViewProps { addDocument: (document: Doc) => boolean; @@ -126,6 +128,8 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { @undoBatch @action protected drop(e: Event, de: DragManager.DropEvent): boolean { + (this.props.Document.dropConverter instanceof ScriptField) && + this.props.Document.dropConverter.script.run({ dragData: de.data }); if (de.data instanceof DragManager.DocumentDragData && !de.data.applyAsTemplate) { if (de.mods === "AltKey" && de.data.draggedDocuments.length) { this.childDocs.map(doc => diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index bff8ce5c1..7d0c900a6 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -10,7 +10,6 @@ width:100%; position: relative; top:0; - padding-top: 20px; padding-left: 10px; padding-right: 10px; background: $light-color-secondary; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 2c77c4b5b..5d88e6290 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -576,7 +576,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc); return !this.childDocs ? (null) : (
    this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()} onDrop={this.onTreeDrop} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index c5bc2e7fb..962fe2a1c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -31,7 +31,7 @@ export class CollectionFreeFormLinkView extends React.Component(["dropAction"]), dragFactory: data.dragFactory + backgroundColor: data.backgroundColor, removeDropProperties: new List(["dropAction"]), dragFactory: data.dragFactory, })); } @@ -69,7 +70,8 @@ export class CurrentUserUtils { static setupCreatePanel(sidebarContainer: Doc, doc: Doc) { // setup a masonry view of all he creators const dragCreators = Docs.Create.MasonryDocument(CurrentUserUtils.setupCreatorButtons(doc), { - width: 500, autoHeight: true, columnWidth: 35, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons" + width: 500, autoHeight: true, columnWidth: 35, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", + dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), yMargin: 0 }); // setup a color picker const color = Docs.Create.ColorDocument({ @@ -81,7 +83,7 @@ export class CurrentUserUtils { panel: Docs.Create.StackingDocument([dragCreators, color], { width: 500, height: 800, chromeStatus: "disabled", title: "creator stack" }), - onClick: ScriptField.MakeScript("this.targetContainer.proto = this.panel") + onClick: ScriptField.MakeScript("this.targetContainer.proto = this.panel"), }); } @@ -148,7 +150,8 @@ export class CurrentUserUtils { doc.expandingButtons = Docs.Create.LinearDocument([doc.undoBtn as Doc, doc.redoBtn as Doc], { title: "expanding buttons", gridGap: 5, xMargin: 5, yMargin: 5, height: 42, width: 100, boxShadow: "0 0", - backgroundColor: "black", preventTreeViewOpen: true, forceActive: true, lockedPosition: true, convertToButtons: true, + backgroundColor: "black", preventTreeViewOpen: true, forceActive: true, lockedPosition: true, + dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }) }); } -- cgit v1.2.3-70-g09d2 From d8f12dade0d0b336e4328fa1f00a1a538588d34e Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 20 Oct 2019 13:34:25 -0400 Subject: initial version of template refactoring --- src/client/documents/Documents.ts | 10 +-- src/client/views/DocumentDecorations.tsx | 26 ++++---- src/client/views/TemplateMenu.tsx | 3 +- .../views/collections/CollectionDockingView.tsx | 23 +++---- .../views/collections/CollectionSchemaView.tsx | 13 ++-- .../views/collections/CollectionStackingView.tsx | 37 +++++++---- src/client/views/collections/CollectionSubView.tsx | 11 ++-- .../views/collections/CollectionTreeView.tsx | 19 +++--- .../CollectionFreeFormLayoutEngines.tsx | 3 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 11 ++-- src/client/views/nodes/DocumentView.tsx | 77 +++++++--------------- src/client/views/nodes/FormattedTextBox.tsx | 3 +- src/client/views/nodes/ImageBox.tsx | 52 ++++++++------- src/client/views/nodes/PDFBox.tsx | 10 +-- src/client/views/nodes/VideoBox.tsx | 35 +++++----- src/client/views/nodes/WebBox.tsx | 11 ++-- src/client/views/pdf/PDFViewer.tsx | 40 +++++------ .../views/presentationview/PresElementBox.tsx | 9 +-- src/client/views/search/SearchBox.tsx | 2 +- src/client/views/search/SearchItem.tsx | 2 +- src/new_fields/Doc.ts | 37 +++++------ .../authentication/models/current_user_utils.ts | 2 +- 24 files changed, 211 insertions(+), 231 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 32fda1954..fbf45816a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -300,7 +300,7 @@ export namespace Docs { */ export namespace Create { - const delegateKeys = ["x", "y", "width", "height", "panX", "panY", "dropAction", "forceActive"]; + const delegateKeys = ["x", "y", "width", "height", "panX", "panY", "nativeWidth", "nativeHeight", "dropAction", "forceActive", "fitWidth"]; /** * This function receives the relevant document prototype and uses @@ -369,11 +369,11 @@ export namespace Docs { requestImageSize(target) .then((size: any) => { let aspect = size.height / size.width; - if (!inst.proto!.nativeWidth) { - inst.proto!.nativeWidth = size.width; + if (!inst.nativeWidth) { + inst.nativeWidth = size.width; } - inst.proto!.nativeHeight = Number(inst.proto!.nativeWidth!) * aspect; - inst.proto!.height = NumCast(inst.proto!.width) * aspect; + inst.nativeHeight = NumCast(inst.nativeWidth) * aspect; + inst.height = NumCast(inst.width) * aspect; }) .catch((err: any) => console.log(err)); // } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 07af4799b..252f90d46 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -19,7 +19,7 @@ import { CollectionView } from "./collections/CollectionView"; import { DocumentButtonBar } from './DocumentButtonBar'; import './DocumentDecorations.scss'; import { PositionDocument } from './nodes/CollectionFreeFormDocumentView'; -import { DocumentView, swapViews } from "./nodes/DocumentView"; +import { DocumentView } from "./nodes/DocumentView"; import { FieldView } from "./nodes/FieldView"; import { FormattedTextBox } from "./nodes/FormattedTextBox"; import { IconBox } from "./nodes/IconBox"; @@ -107,10 +107,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> fieldTemplate.title = metaKey; Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate)); if (text.startsWith(">>")) { - let layoutNative = Doc.MakeTitled("layoutNative"); - Doc.GetProto(docTemplate).layoutNative = layoutNative; - swapViews(fieldTemplate, "", "layoutNative", layoutNative); - layoutNative.layout = StrCast(fieldTemplateView.props.Document.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); + Doc.GetProto(docTemplate).layout = StrCast(fieldTemplateView.props.Document.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); } } } @@ -471,8 +468,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { let doc = PositionDocument(element.props.Document); let layoutDoc = PositionDocument(Doc.Layout(element.props.Document)); - let nwidth = doc.nativeWidth || 0; - let nheight = doc.nativeHeight || 0; + let nwidth = layoutDoc.nativeWidth || 0; + let nheight = layoutDoc.nativeHeight || 0; let width = (layoutDoc.width || 0); let height = (layoutDoc.height || (nheight / nwidth * width)); let scale = element.props.ScreenToLocalTransform().Scale * element.props.ContentScaling(); @@ -480,21 +477,20 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let actualdH = Math.max(height + (dH * scale), 20); doc.x = (doc.x || 0) + dX * (actualdW - width); doc.y = (doc.y || 0) + dY * (actualdH - height); - let proto = doc.isTemplateField ? doc : Doc.GetProto(element.props.Document); // bcz: 'doc' didn't work here... let fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight); if (fixedAspect && e.ctrlKey && layoutDoc.ignoreAspect) { layoutDoc.ignoreAspect = false; - proto.nativeWidth = nwidth = layoutDoc.width || 0; - proto.nativeHeight = nheight = layoutDoc.height || 0; + layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0; + layoutDoc.nativeHeight = nheight = layoutDoc.height || 0; } if (fixedAspect && (!nwidth || !nheight)) { - proto.nativeWidth = nwidth = layoutDoc.width || 0; - proto.nativeHeight = nheight = layoutDoc.height || 0; + layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0; + layoutDoc.nativeHeight = nheight = layoutDoc.height || 0; } if (nwidth > 0 && nheight > 0 && !layoutDoc.ignoreAspect) { if (Math.abs(dW) > Math.abs(dH)) { if (!fixedAspect) { - Doc.SetInPlace(doc, "nativeWidth", actualdW / (layoutDoc.width || 1) * (layoutDoc.nativeWidth || 0), true); + layoutDoc.nativeWidth = actualdW / (layoutDoc.width || 1) * (layoutDoc.nativeWidth || 0); } layoutDoc.width = actualdW; if (fixedAspect && !layoutDoc.fitWidth) layoutDoc.height = nheight / nwidth * layoutDoc.width; @@ -502,7 +498,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } else { if (!fixedAspect) { - Doc.SetInPlace(doc, "nativeHeight", actualdH / (layoutDoc.height || 1) * (doc.nativeHeight || 0), true); + layoutDoc.nativeHeight = actualdH / (layoutDoc.height || 1) * (doc.nativeHeight || 0); } layoutDoc.height = actualdH; if (fixedAspect && !layoutDoc.fitWidth) layoutDoc.width = nwidth / nheight * layoutDoc.height; @@ -511,7 +507,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } else { dW && (layoutDoc.width = actualdW); dH && (layoutDoc.height = actualdH); - dH && layoutDoc.autoHeight && Doc.SetInPlace(layoutDoc, "autoHeight", false, true); + dH && layoutDoc.autoHeight && (layoutDoc.autoHeight = false); } } }); diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index d76b033f0..96265385e 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -9,6 +9,7 @@ import { DocumentView } from "./nodes/DocumentView"; import { Template, Templates } from "./Templates"; import React = require("react"); import { Doc } from "../../new_fields/Doc"; +import { StrCast } from "../../new_fields/Types"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -128,7 +129,7 @@ export class TemplateMenu extends React.Component { this.props.templates.forEach((checked, template) => templateMenu.push()); templateMenu.push(); - templateMenu.push(); + templateMenu.push(); templateMenu.push(); return (
    diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 1f78c8c97..42d372f4a 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -615,19 +615,20 @@ export class DockedFrameRenderer extends React.Component { } } - panelWidth = () => this._document && this._document.maxWidth ? Math.min(Math.max(NumCast(this._document.width), NumCast(this._document.nativeWidth)), this._panelWidth) : this._panelWidth; + get layoutDoc() { return this._document && Doc.Layout(this._document);} + panelWidth = () => this.layoutDoc && this.layoutDoc.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc.width), NumCast(this.layoutDoc.nativeWidth)), this._panelWidth) : this._panelWidth; panelHeight = () => this._panelHeight; - nativeWidth = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0; - nativeHeight = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0; + nativeWidth = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!.fitWidth ? NumCast(this.layoutDoc!.nativeWidth) || this._panelWidth : 0; + nativeHeight = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!.fitWidth ? NumCast(this.layoutDoc!.nativeHeight) || this._panelHeight : 0; contentScaling = () => { - if (this._document!.type === DocumentType.PDF) { - if ((this._document && this._document.fitWidth) || - this._panelHeight / NumCast(this._document!.nativeHeight) > this._panelWidth / NumCast(this._document!.nativeWidth)) { - return this._panelWidth / NumCast(this._document!.nativeWidth); + if (this.layoutDoc!.type === DocumentType.PDF) { + if ((this.layoutDoc && this.layoutDoc.fitWidth) || + this._panelHeight / NumCast(this.layoutDoc!.nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!.nativeWidth)) { + return this._panelWidth / NumCast(this.layoutDoc!.nativeWidth); } else { - return this._panelHeight / NumCast(this._document!.nativeHeight); + return this._panelHeight / NumCast(this.layoutDoc!.nativeHeight); } } const nativeH = this.nativeHeight(); @@ -645,7 +646,7 @@ export class DockedFrameRenderer extends React.Component { } return Transform.Identity(); } - get previewPanelCenteringOffset() { return this.nativeWidth() && !this._document!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; } + get previewPanelCenteringOffset() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; } addDocTab = (doc: Doc, dataDoc: Opt, location: string) => { SelectionManager.DeselectAll(); @@ -690,11 +691,11 @@ export class DockedFrameRenderer extends React.Component { } render() { - return (!this._isActive || !this._document) ? (null) : + return (!this._isActive || !this.layoutDoc) ? (null) : (
    this._mainCont = ref} style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`, - height: this._document && this._document.fitWidth ? undefined : "100%" + height: this.layoutDoc && this.layoutDoc.fitWidth ? undefined : "100%" }}> {this.docView}
    ); diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 3218f630a..23e070750 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -922,8 +922,9 @@ interface CollectionSchemaPreviewProps { export class CollectionSchemaPreview extends React.Component{ private dropDisposer?: DragManager.DragDropDisposer; _mainCont?: HTMLDivElement; - private get nativeWidth() { return NumCast(this.props.Document!.nativeWidth, this.props.PanelWidth()); } - private get nativeHeight() { return NumCast(this.props.Document!.nativeHeight, this.props.PanelHeight()); } + private get layoutDoc() { return this.props.Document && Doc.Layout(this.props.Document); } + private get nativeWidth() { return NumCast(this.layoutDoc!.nativeWidth, this.props.PanelWidth()); } + private get nativeHeight() { return NumCast(this.layoutDoc!.nativeHeight, this.props.PanelHeight()); } private contentScaling = () => { let wscale = this.props.PanelWidth() / (this.nativeWidth ? this.nativeWidth : this.props.PanelWidth()); if (wscale * this.nativeHeight > this.props.PanelHeight()) { @@ -947,10 +948,8 @@ export class CollectionSchemaPreview extends React.Component { let target = Doc.GetProto(otherdoc); - let layoutNative = Doc.MakeTitled("layoutNative"); - layoutNative.layout = ComputedField.MakeFunction("this.image_data[0]"); - target.layoutNative = layoutNative; - target.layoutCUstom = target.layout = Doc.MakeDelegate(de.data.draggedDocuments[0]); + target.layout = ComputedField.MakeFunction("this.image_data[0]"); + target.layoutCustom = Doc.MakeDelegate(de.data.draggedDocuments[0]); }); e.stopPropagation(); } @@ -968,7 +967,7 @@ export class CollectionSchemaPreview extends React.Component doc) { this._docXfs.length = 0; return docs.map((d, i) => { let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d); - let width = () => Math.min(d.nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns); - let height = () => this.getDocHeight(pair.layout); + let layoutDoc = pair.layout ? Doc.Layout(pair.layout) : d; + let width = () => Math.min(layoutDoc.nativeWidth && !layoutDoc.ignoreAspect && !this.props.Document.fillColumn ? layoutDoc[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns); + let height = () => this.getDocHeight(layoutDoc); let dref = React.createRef(); - let dxf = () => this.getDocTransform(pair.layout!, dref.current!); + let dxf = () => this.getDocTransform(layoutDoc, dref.current!); this._docXfs.push({ dxf: dxf, width: width, height: height }); let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap); let style = this.isStackingView ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` }; return
    - {pair.layout instanceof Doc && this.getDisplayDoc(pair.layout, pair.data, dxf, width)} + {this.getDisplayDoc(pair.layout || d, pair.data, dxf, width)}
    ; }); } @@ -111,8 +112,15 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { if (this.props.Document.autoHeight) { let sectionsList = Array.from(this.Sections.size ? this.Sections.values() : [this.filteredChildren]); if (this.isStackingView) { - return this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => Math.max(maxHght, - (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap), this.yMargin)), 0); + let res = this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => { + let r1 = Math.max(maxHght, + (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => { + let val = height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap); + return val; + }, this.yMargin)); + return r1; + }, 0); + return res; } else { let sum = Array.from(this._heightMap.values()).reduce((acc: number, curr: number) => acc += curr, 0); return this.props.ContentScaling() * (sum + (this.Sections.size ? 85 : -15)); @@ -122,7 +130,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { }, (hgt: number) => { let doc = hgt === -1 ? undefined : this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc; - doc && (doc.height = hgt); + doc && (Doc.Layout(doc).height = hgt); }, { fireImmediately: true } ); @@ -155,11 +163,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); } @computed get onClickHandler() { return ScriptCast(this.Document.onChildClick); } - getDisplayDoc(layoutDoc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) { - let height = () => this.getDocHeight(layoutDoc); + getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) { + let layoutDoc = Doc.Layout(doc); + let height = () => this.getDocHeight(doc); let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]()); return doc) { getDocHeight(d?: Doc) { if (!d) return 0; let layoutDoc = Doc.Layout(d); - let nw = NumCast(d.nativeWidth); - let nh = NumCast(d.nativeHeight); + let nw = NumCast(layoutDoc.nativeWidth); + let nh = NumCast(layoutDoc.nativeHeight); let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1); if (!layoutDoc.ignoreAspect && !layoutDoc.fitWidth && nw && nh) { let aspect = nw && nh ? nh / nw : 1; if (!(d.nativeWidth && !layoutDoc.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid); return wid * aspect; } - return layoutDoc.fitWidth ? !d.nativeHeight ? this.props.PanelHeight() - 2 * this.yMargin : - Math.min(wid * NumCast(layoutDoc.scrollHeight, NumCast(d.nativeHeight)) / NumCast(d.nativeWidth, 1), this.props.PanelHeight() - 2 * this.yMargin) : layoutDoc[HeightSym](); + return layoutDoc.fitWidth ? !layoutDoc.nativeHeight ? this.props.PanelHeight() - 2 * this.yMargin : + Math.min(wid * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc.nativeHeight)) / NumCast(layoutDoc.nativeWidth, 1), this.props.PanelHeight() - 2 * this.yMargin) : layoutDoc[HeightSym](); } columnDividerDown = (e: React.PointerEvent) => { diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index f76447157..0e8c46d40 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -61,11 +61,11 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { this._childLayoutDisposer = reaction(() => [this.childDocs, Cast(this.props.Document.childLayout, Doc)], async (args) => { if (args[1] instanceof Doc) { - this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc))); + this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc), "layoutFromParent")); + } + else if (!(args[1] instanceof Promise)) { + this.childDocs.filter(d => !d.isTemplateField).map(async doc => doc.layoutKey === "layoutFromParent" && (doc.layoutKey = "layout")); } - // else if (!(args[1] instanceof Promise)) { - // this.childDocs.filter(d => !d.isTemplateField).map(async doc => doc.layout = undefined); - // } }); } @@ -134,8 +134,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { if (de.data instanceof DragManager.DocumentDragData && !de.data.applyAsTemplate) { if (de.mods === "AltKey" && de.data.draggedDocuments.length) { this.childDocs.map(doc => - Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc) - ); + Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc, "layoutFromParent")); e.stopPropagation(); return true; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 2fbe8527e..77736b7d0 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -249,23 +249,22 @@ class TreeView extends React.Component { let finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1]); return finalXf; } + get layoutDoc() { return Doc.Layout(this.props.document); } docWidth = () => { - let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth); - let layoutDoc = Doc.Layout(this.props.document); - if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20)); - return NumCast(this.props.document.nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20; + let aspect = NumCast(this.layoutDoc.nativeHeight) / NumCast(this.layoutDoc.nativeWidth); + if (aspect) return Math.min(this.layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20)); + return NumCast(this.layoutDoc.nativeWidth) ? Math.min(this.layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20; } docHeight = () => { let bounds = this.boundsOfCollectionDocument; return Math.min(this.MAX_EMBED_HEIGHT, (() => { - let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth); - let layoutDoc = Doc.Layout(this.props.document); + let aspect = NumCast(this.layoutDoc.nativeHeight) / NumCast(this.layoutDoc.nativeWidth); if (aspect) return this.docWidth() * aspect; if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x); - return layoutDoc.fitWidth ? (!this.props.document.nativeHeight ? NumCast(this.props.containingCollection.height) : - Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(this.props.document.nativeHeight)) / NumCast(this.props.document.nativeWidth, + return this.layoutDoc.fitWidth ? (!this.props.document.nativeHeight ? NumCast(this.props.containingCollection.height) : + Math.min(this.docWidth() * NumCast(this.layoutDoc.scrollHeight, NumCast(this.layoutDoc.nativeHeight)) / NumCast(this.layoutDoc.nativeWidth, NumCast(this.props.containingCollection.height)))) : - NumCast(layoutDoc.height) ? NumCast(layoutDoc.height) : 50; + NumCast(this.layoutDoc.height) ? NumCast(this.layoutDoc.height) : 50; })()); } @@ -484,7 +483,7 @@ class TreeView extends React.Component { }; const childLayout = Doc.Layout(pair.layout); let rowHeight = () => { - let aspect = NumCast(child.nativeWidth, 0) / NumCast(child.nativeHeight, 0); + let aspect = NumCast(childLayout.nativeWidth, 0) / NumCast(childLayout.nativeHeight, 0); return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym](); }; return = numCols) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index eff73b14e..aa1106f13 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -63,10 +63,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private _hitCluster = false; @observable _clusterSets: (Doc[])[] = []; + get layoutDoc() { return PanZoomDocument(Doc.Layout(this.props.Document)); } @computed get fitToContent() { return (this.props.fitToBox || this.Document.fitToBox) && !this.isAnnotationOverlay; } @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent && !this.isAnnotationOverlay ? this.props.ContentScaling() : 1; } @computed get contentBounds() { return aggregateBounds(this.elements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!)); } - @computed get nativeWidth() { return this.fitToContent ? 0 : this.Document.nativeWidth || 0; } + @computed get nativeWidth() { return this.layoutDoc.fitToContent ? 0 : this.layoutDoc.nativeWidth || 0; } @computed get nativeHeight() { return this.fitToContent ? 0 : this.Document.nativeHeight || 0; } private get isAnnotationOverlay() { return this.props.fieldExt ? true : false; } // fieldExt will be "" or "annotation". should maybe generalize this, or make it more specific (ie, 'annotation' instead of 'fieldExt') private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; } @@ -448,6 +449,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ...this.props, DataDoc: childData, Document: childLayout, + layoutKey: undefined, ruleProvider: this.Document.isRuleProvider && childLayout.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider, //bcz: hack! - currently ruleProviders apply to documents in nested colleciton, not direct children of themselves onClick: undefined, // this.props.onClick, // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform, diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 2243a44d5..e3ca02fa4 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -77,7 +77,7 @@ export class CollectionFreeFormDocumentView extends DocComponent { let ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined; - let ld = this.layoutDoc.layout instanceof Doc ? this.layoutDoc.layout : undefined; + let ld = this.layoutDoc[StrCast(this.layoutDoc.layoutKey, "layout")] instanceof Doc ? this.layoutDoc[StrCast(this.layoutDoc.layoutKey, "layout")] as Doc : undefined; let br = StrCast((ld || this.props.Document).borderRounding); br = !br && ruleRounding ? ruleRounding : br; if (br.endsWith("%")) { diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index bb9315ca3..7aed416f4 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -2,7 +2,7 @@ import { computed } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../../new_fields/Doc"; import { ScriptField } from "../../../new_fields/ScriptField"; -import { Cast } from "../../../new_fields/Types"; +import { Cast, StrCast } from "../../../new_fields/Types"; import { OmitKeys, Without } from "../../../Utils"; import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox"; import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox"; @@ -69,10 +69,9 @@ export class DocumentContentsView extends React.Component obj.active = this.props.parentActive).omit, Document: this.layoutDoc, - DataDoc: this.dataDoc + DataDoc: this.dataDoc, }; return { props: list }; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 1087c57ad..2d13eeae4 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -191,9 +191,8 @@ export class DocumentView extends DocComponent(Docu let preventDefault = true; if (this._doubleTap && this.props.renderDepth && (!this.onClickHandler || !this.onClickHandler.script)) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click let fullScreenAlias = Doc.MakeAlias(this.props.Document); - let layoutNative = await PromiseValue(Cast(this.props.Document.layoutNative, Doc)); - if (layoutNative && fullScreenAlias.layout === layoutNative.layout) { - await swapViews(fullScreenAlias, "layoutCustom", "layoutNative"); + if (StrCast(fullScreenAlias.layoutKey) !== "layoutCustom" && fullScreenAlias["layoutCustom"] !== undefined) { + fullScreenAlias.layoutKey = "layoutCustom"; } this.props.addDocTab(fullScreenAlias, undefined, "inTab"); SelectionManager.DeselectAll(); @@ -287,16 +286,13 @@ export class DocumentView extends DocComponent(Docu @undoBatch deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument && this.props.removeDocument(this.props.Document); } - static makeNativeViewClicked = async (doc: Doc): Promise => { - undoBatch(() => swapViews(doc, "layoutNative", "layoutCustom"))(); + static makeNativeViewClicked = (doc: Doc) => { + undoBatch(() => doc.layoutKey = "layout")(); } - static makeCustomViewClicked = async (doc: Doc, dataDoc: Opt) => { + static makeCustomViewClicked = (doc: Doc, dataDoc: Opt) => { const batch = UndoManager.StartBatch("CustomViewClicked"); if (doc.layoutCustom === undefined) { - Doc.GetProto(dataDoc || doc).layoutNative = Doc.MakeTitled("layoutNative"); - await swapViews(doc, "", "layoutNative"); - const width = NumCast(doc.width); const height = NumCast(doc.height); const options = { title: "data", width, x: -width / 2, y: - height / 2, }; @@ -326,10 +322,9 @@ export class DocumentView extends DocComponent(Docu let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: doc.title + "_layout", width: width + 20, height: Math.max(100, height + 45) }); Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate), true); - Doc.ApplyTemplateTo(docTemplate, doc, undefined); - Doc.GetProto(dataDoc || doc).layoutCustom = Doc.MakeTitled("layoutCustom"); + Doc.ApplyTemplateTo(docTemplate, dataDoc || doc, "layoutCustom", undefined); } else { - await swapViews(doc, "layoutCustom", "layoutNative"); + doc.layoutKey = "layoutCustom"; } batch.end(); } @@ -356,7 +351,7 @@ export class DocumentView extends DocComponent(Docu DocUtils.MakeLink({ doc: de.data.annotationDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, `Link from ${StrCast(de.data.annotationDocument.title)}`); } if (de.data instanceof DragManager.DocumentDragData && de.data.applyAsTemplate) { - Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document); + Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document, "layoutCustom"); e.stopPropagation(); } if (de.data instanceof DragManager.LinkDragData) { @@ -383,12 +378,11 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action freezeNativeDimensions = (): void => { - let proto = this.Document.isTemplateDoc ? this.props.Document : Doc.GetProto(this.props.Document); - proto.autoHeight = this.Document.autoHeight = false; - proto.ignoreAspect = !proto.ignoreAspect; - if (!proto.ignoreAspect && !proto.nativeWidth) { - proto.nativeWidth = this.props.PanelWidth(); - proto.nativeHeight = this.props.PanelHeight(); + this.layoutDoc.autoHeight = this.layoutDoc.autoHeight = false; + this.layoutDoc.ignoreAspect = !this.layoutDoc.ignoreAspect; + if (!this.layoutDoc.ignoreAspect && !this.layoutDoc.nativeWidth) { + this.layoutDoc.nativeWidth = this.props.PanelWidth(); + this.layoutDoc.nativeHeight = this.props.PanelHeight(); } } @@ -505,7 +499,7 @@ export class DocumentView extends DocComponent(Docu layoutItems.push({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" }); if (this.Document.type !== DocumentType.COL && this.Document.type !== DocumentType.TEMPLATE) { layoutItems.push({ description: "Use Custom Layout", event: () => DocumentView.makeCustomViewClicked(this.props.Document, this.props.DataDoc), icon: "concierge-bell" }); - } else if (this.props.Document.layoutNative) { + } else { layoutItems.push({ description: "Use Native Layout", event: () => DocumentView.makeNativeViewClicked(this.props.Document), icon: "concierge-bell" }); } !existing && cm.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" }); @@ -599,7 +593,7 @@ export class DocumentView extends DocComponent(Docu return (showTitle ? 25 : 0) + 1; } - childScaling = () => (this.props.Document.fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling()); + childScaling = () => (this.layoutDoc.fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling()); @computed get contents() { return ((Docu this.props.backgroundColor(this.Document) || StrCast(this.layoutDoc.backgroundColor) : ruleColor && !colorSet ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.Document); - const nativeWidth = this.props.Document.fitWidth ? this.props.PanelWidth() - 2 : this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%"; - const nativeHeight = this.props.Document.fitWidth ? this.props.PanelHeight() - 2 : this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; + const nativeWidth = this.layoutDoc.fitWidth ? this.props.PanelWidth() - 2 : this.nativeWidth > 0 && !this.layoutDoc.ignoreAspect ? `${this.nativeWidth}px` : "100%"; + const nativeHeight = this.layoutDoc.fitWidth ? this.props.PanelHeight() - 2 : this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined; const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : this.getLayoutPropStr("showTitle"); const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.getLayoutPropStr("showCaption"); @@ -686,7 +680,7 @@ export class DocumentView extends DocComponent(Docu const highlightColors = ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"]; const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid", "solid"]; - let highlighting = fullDegree && this.props.Document.type !== DocumentType.FONTICON && this.props.Document.viewType !== CollectionViewType.Linear; + let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc.viewType !== CollectionViewType.Linear; return (
    (Docu color: StrCast(this.Document.color), outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px", border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined, - background: this.props.Document.type === DocumentType.FONTICON || this.props.Document.viewType === CollectionViewType.Linear ? undefined : backgroundColor, + background: this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc.viewType === CollectionViewType.Linear ? undefined : backgroundColor, width: animwidth, height: animheight, - transform: `scale(${this.props.Document.fitWidth ? 1 : this.props.ContentScaling()})`, + transform: `scale(${this.layoutDoc.fitWidth ? 1 : this.props.ContentScaling()})`, opacity: this.Document.opacity }} onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} @@ -707,7 +701,7 @@ export class DocumentView extends DocComponent(Docu > {this.props.Document.links && DocListCast(this.props.Document.links).map((d, i) => //this.linkEndpointDoc(d).type === DocumentType.PDFANNO ? (null) : -
    +
    )} {!showTitle && !showCaption ? @@ -733,33 +727,6 @@ export class DocumentView extends DocComponent(Docu } } -export async function swapViews(doc: Doc, newLayoutField: string, oldLayoutField: string, oldLayout?: Doc) { - let oldLayoutExt = oldLayout || await Cast(doc[oldLayoutField], Doc); - if (oldLayoutExt) { - oldLayoutExt.autoHeight = doc.autoHeight; - oldLayoutExt.width = doc.width; - oldLayoutExt.height = doc.height; - oldLayoutExt.nativeWidth = doc.nativeWidth; - oldLayoutExt.nativeHeight = doc.nativeHeight; - oldLayoutExt.ignoreAspect = doc.ignoreAspect; - oldLayoutExt.type = doc.type; - oldLayoutExt.layout = doc.layout; - } - - let newLayoutExt = newLayoutField && await Cast(doc[newLayoutField], Doc); - if (newLayoutExt) { - doc.autoHeight = newLayoutExt.autoHeight; - doc.width = newLayoutExt.width; - doc.height = newLayoutExt.height; - doc.nativeWidth = newLayoutExt.nativeWidth; - doc.nativeHeight = newLayoutExt.nativeHeight; - doc.ignoreAspect = newLayoutExt.ignoreAspect; - doc.type = newLayoutExt.type; - doc.layout = await newLayoutExt.layout; - } -} - Scripting.addGlobal(function toggleDetail(doc: any) { - let native = typeof doc.layout === "string"; - swapViews(doc, native ? "layoutCustom" : "layoutNative", native ? "layoutNative" : "layoutCustom"); + doc.layoutKey = StrCast(doc.layoutKey, "layout") === "layout" ? "layoutCustom" : "layout"; }); \ No newline at end of file diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index ac10d729a..f866e6f7e 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -271,7 +271,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe newLayout = Doc.MakeDelegate(draggedDoc); newLayout.layout = StrCast(newLayout.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${this.props.fieldKey}"}`); } - this.props.Document.layout = newLayout; + this.props.Document.layoutCustom = newLayout; + this.props.Document.layoutKey = "layoutCustom"; e.stopPropagation(); // embed document when dragging with a userDropAction or an embedDoc flag set } else if (de.data.userDropAction || de.data.embedDoc) { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 2fc4c04e6..dc80af268 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -2,7 +2,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faEye } from '@fortawesome/free-regular-svg-icons'; import { faAsterisk, faFileAudio, faImage, faPaintBrush } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, runInAction } from 'mobx'; +import { action, computed, observable, runInAction, trace } from 'mobx'; import { observer } from "mobx-react"; import Lightbox from 'react-image-lightbox'; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app @@ -13,7 +13,7 @@ import { ComputedField } from '../../../new_fields/ScriptField'; import { BoolCast, Cast, FieldValue, NumCast, StrCast } from '../../../new_fields/Types'; import { AudioField, ImageField } from '../../../new_fields/URLField'; import { RouteStore } from '../../../server/RouteStore'; -import { Utils, returnOne, emptyFunction } from '../../../Utils'; +import { Utils, returnOne, emptyFunction, OmitKeys } from '../../../Utils'; import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; @@ -159,7 +159,7 @@ export class ImageBox extends DocAnnotatableComponent { - let proto = Doc.GetProto(this.props.Document); - let nw = this.props.Document.nativeWidth; - let nh = this.props.Document.nativeHeight; - let w = this.props.Document.width; - let h = this.props.Document.height; - proto.rotation = (NumCast(this.props.Document.rotation) + 90) % 360; - proto.nativeWidth = nh; - proto.nativeHeight = nw; - this.props.Document.width = h; - this.props.Document.height = w; + let nw = this.layoutDoc.nativeWidth; + let nh = this.layoutDoc.nativeHeight; + let w = this.layoutDoc.width; + let h = this.layoutDoc.height; + this.layoutDoc.rotation = (NumCast(this.layoutDoc.rotation) + 90) % 360; + this.layoutDoc.nativeWidth = nh; + this.layoutDoc.nativeHeight = nw; + this.layoutDoc.width = h; + this.layoutDoc.height = w; }); specificContextMenu = (e: React.MouseEvent): void => { @@ -251,7 +251,7 @@ export class ImageBox extends DocAnnotatableComponent @@ -379,16 +379,17 @@ export class ImageBox extends DocAnnotatableComponent 20) { @@ -401,14 +402,14 @@ export class ImageBox extends DocAnnotatableComponent - {() => [this.content]} + {() => [this.content(this.layoutDoc)]}
    ); } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index f31128356..f3ea8328b 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; import 'react-image-lightbox/style.css'; -import { Opt, WidthSym } from "../../../new_fields/Doc"; +import { Opt, WidthSym, Doc } from "../../../new_fields/Doc"; import { makeInterface } from "../../../new_fields/Schema"; import { ScriptField } from '../../../new_fields/ScriptField'; import { Cast } from "../../../new_fields/Types"; @@ -65,11 +65,13 @@ export class PDFBox extends DocAnnotatableComponent }, { fireImmediately: true }); } + get layoutDoc() { return Doc.Layout(this.props.Document); } + loaded = (nw: number, nh: number, np: number) => { this.dataDoc.numPages = np; - this.Document.nativeWidth = nw * 96 / 72; - this.Document.nativeHeight = nh * 96 / 72; - !this.Document.fitWidth && !this.Document.ignoreAspect && (this.Document.height = this.Document[WidthSym]() * (nh / nw)); + this.layoutDoc.nativeWidth = nw * 96 / 72; + this.layoutDoc.nativeHeight = nh * 96 / 72; + !this.layoutDoc.fitWidth && !this.layoutDoc.ignoreAspect && (this.layoutDoc.height = this.layoutDoc[WidthSym]() * (nh / nw)); } public search(string: string, fwd: boolean) { this._pdfViewer && this._pdfViewer.search(string, fwd); } diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 19968e6e1..4c01279ae 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -55,14 +55,15 @@ export class VideoBox extends DocAnnotatableComponent { let aspect = this.player!.videoWidth / this.player!.videoHeight; - var nativeWidth = FieldValue(this.Document.nativeWidth, 0); - var nativeHeight = FieldValue(this.Document.nativeHeight, 0); + var nativeWidth = NumCast(this.layoutDoc.nativeWidth, 0); + var nativeHeight = NumCast(this.layoutDoc.nativeHeight, 0); if (!nativeWidth || !nativeHeight) { - if (!this.Document.nativeWidth) this.Document.nativeWidth = this.player!.videoWidth; - this.Document.nativeHeight = this.Document.nativeWidth / aspect; - this.Document.height = FieldValue(this.Document.width, 0) / aspect; + if (!this.layoutDoc.nativeWidth) this.layoutDoc.nativeWidth = this.player!.videoWidth; + this.layoutDoc.nativeHeight = NumCast(this.layoutDoc.nativeWidth) / aspect; + this.layoutDoc.height = NumCast(this.layoutDoc.width, 0) / aspect; } if (!this.Document.duration) this.Document.duration = this.player!.duration; } @@ -107,7 +108,7 @@ export class VideoBox extends DocAnnotatableComponent {"" + Math.round(curTime)} {" " + Math.round((curTime - Math.trunc(curTime)) * 100)} @@ -312,7 +313,7 @@ export class VideoBox extends DocAnnotatableComponent { this._isResetClick += Math.abs(e.movementX) + Math.abs(e.movementY); - this.Seek(Math.max(0, NumCast(this.props.Document.currentTimecode, 0) + Math.sign(e.movementX) * 0.0333)); + this.Seek(Math.max(0, NumCast(this.layoutDoc.currentTimecode, 0) + Math.sign(e.movementX) * 0.0333)); e.stopImmediatePropagation(); } @action @@ -320,10 +321,10 @@ export class VideoBox extends DocAnnotatableComponent Math.round(this.Document.currentTimecode || 0)); return