From 43e573ea0cf4634b65b513c633f90be84846f8df Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 29 Apr 2020 21:08:37 -0400 Subject: changed detailedView template to be a layoutstring. fixed text boxes to allow new templates from templated text boxes. fixed __LAYOUT__ to work with comound layout strings --- src/client/documents/Documents.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 811bb5fb2..0d1d73ca3 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -541,8 +541,23 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COLOR), "", options); } - export function TextDocument(text: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.RTF), text, options, undefined, "text"); + export function TextDocument(text: string, options: DocumentOptions = {}, fieldKey: string = "text") { + const rtf = { + doc: { + type: "doc", content: [{ + type: "paragraph", + content: [{ + type: "text", + text + }] + }] + }, + selection: { type: "text", anchor: 1, head: 1 }, + storedMarks: [] + }; + + const field = text ? new RichTextField(JSON.stringify(rtf), text) : undefined; + return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey); } export function LinkDocument(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, options: DocumentOptions = {}, id?: string) { -- cgit v1.2.3-70-g09d2 From 0f4b9e541e4e1332bbed95f4a99113162ffef806 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 30 Apr 2020 11:10:27 -0400 Subject: added double click script support --- src/client/documents/Documents.ts | 2 ++ src/client/views/ScriptBox.tsx | 4 ++-- .../views/collections/CollectionStackingView.tsx | 7 ++++--- src/client/views/collections/CollectionSubView.tsx | 2 ++ src/client/views/collections/CollectionView.tsx | 7 ++++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 3 ++- .../CollectionMulticolumnView.tsx | 2 ++ .../CollectionMultirowView.tsx | 3 ++- .../views/nodes/ContentFittingDocumentView.tsx | 1 + src/client/views/nodes/DocumentView.tsx | 23 ++++++++++++++++------ src/new_fields/Types.ts | 4 ++-- src/new_fields/documentSchemas.ts | 1 + .../authentication/models/current_user_utils.ts | 8 ++++++-- 13 files changed, 47 insertions(+), 20 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 0d1d73ca3..436e59daf 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -95,6 +95,7 @@ export interface DocumentOptions { hideHeadings?: boolean; // whether stacking view column headings should be hidden isTemplateForField?: string; // the field key for which the containing document is a rendering template isTemplateDoc?: boolean; + scriptKey?: string; // the script key that a child click func script template document will write into templates?: List; backgroundColor?: string | ScriptField; // background color for data doc _backgroundColor?: string | ScriptField; // background color for each template layout doc ( overrides backgroundColor ) @@ -132,6 +133,7 @@ export interface DocumentOptions { activePen?: Doc; // which pen document is currently active (used as the radio button state for the 'unhecked' pen tool scripts) onClick?: ScriptField; onChildClick?: ScriptField; // script given to children of a collection to execute when they are clicked + onChildDoubleClick?: ScriptField; // script given to children of a collection to execute when they are double clicked onPointerDown?: ScriptField; onPointerUp?: ScriptField; dropConverter?: ScriptField; // script to run when documents are dropped on this Document. diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx index 153b81876..66d3b937e 100644 --- a/src/client/views/ScriptBox.tsx +++ b/src/client/views/ScriptBox.tsx @@ -81,9 +81,9 @@ export class ScriptBox extends React.Component { ); } //let l = docList(this.source[0].data).length; if (l) { let ind = this.target[0].index !== undefined ? (this.target[0].index+1) % l : 0; this.target[0].index = ind; this.target[0].proto = getProto(docList(this.source[0].data)[ind]);} - public static EditButtonScript(title: string, doc: Doc, fieldKey: string, clientX: number, clientY: number, contextParams?: { [name: string]: string }) { + public static EditButtonScript(title: string, doc: Doc, fieldKey: string, clientX: number, clientY: number, contextParams?: { [name: string]: string }, defaultScript?: ScriptField) { let overlayDisposer: () => void = emptyFunction; - const script = ScriptCast(doc[fieldKey]); + const script = ScriptCast(doc[fieldKey]) || defaultScript; let originalText: string | undefined = undefined; if (script) { originalText = script.script.originalScript; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index e3720bf01..556d7df5c 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -150,8 +150,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { this.createDashEventsTarget(ele!); //so the whole grid is the drop target? } - @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); } - @computed get onClickHandler() { return ScriptCast(this.Document.onChildClick); } + @computed get onChildClickHandler() { return this.props.childClickScript || ScriptCast(this.Document.onChildClick); } + @computed get onChildDoubleClickHandler() { return this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); } addDocTab = (doc: Doc, where: string) => { if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) { @@ -178,7 +178,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { fitToBox={BoolCast(this.props.Document._freezeChildDimensions)} rootSelected={this.rootSelected} dropAction={StrCast(this.props.Document.childDropAction) as dropActionType} - onClick={layoutDoc.isTemplateDoc ? this.onClickHandler : this.onChildClickHandler} + onClick={this.onChildClickHandler} + onDoubleClick={this.onChildDoubleClickHandler} getTransform={dxf} focus={this.props.focus} CollectionDoc={this.props.CollectionView?.props.Document} diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 1bfd408f8..8cc1af55b 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -43,6 +43,8 @@ export interface CollectionViewProps extends FieldViewProps { export interface SubCollectionViewProps extends CollectionViewProps { CollectionView: Opt; children?: never | (() => JSX.Element[]) | React.ReactNode; + childClickScript?: ScriptField; + childDoubleClickScript?: ScriptField; freezeChildDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height overrideDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox) ignoreFields?: string[]; // used in TreeView to ignore specified fields (see LinkBox) diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 801704673..2c52097aa 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -247,13 +247,14 @@ export class CollectionView extends Touchable { const existingOnClick = ContextMenu.Instance.findByDescription("OnClick..."); const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; - const funcs = [{ key: "onChildClick", name: "On Child Clicked", script: undefined as any as ScriptField }]; + const funcs = [{ key: "onChildClick", name: "On Child Clicked", script: undefined as any as ScriptField }, + { key: "onChildDoubleClick", name: "On Child Double Clicked", script: undefined as any as ScriptField }]; DocListCast(Cast(Doc.UserDoc().childClickFuncs, Doc, null).data).forEach(childClick => - funcs.push({ key: "onChildClick", name: StrCast(childClick.title), script: ScriptCast(childClick.script) })); + funcs.push({ key: StrCast(childClick.scriptKey), name: StrCast(childClick.title), script: ScriptCast(childClick.data) })); funcs.map(func => onClicks.push({ description: `Edit ${func.name} script`, icon: "edit", event: (obj: any) => { func.script && (this.props.Document[func.key] = ObjectField.MakeCopy(func.script)); - ScriptBox.EditButtonScript(func.name + "...", this.props.Document, func.key, obj.x, obj.y, { thisContainer: Doc.name }); + ScriptBox.EditButtonScript(func.name + "...", this.props.Document, func.key, obj.x, obj.y, { thisContainer: Doc.name }, func.script); } })); !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 77de486d9..11d0f298d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -70,7 +70,6 @@ type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchem const PanZoomDocument = makeInterface(panZoomSchema, documentSchema, positionSchema, pageSchema); export type collectionFreeformViewProps = { forceScaling?: boolean; // whether to force scaling of content (needed by ImageBox) - childClickScript?: ScriptField; viewDefDivClick?: ScriptField; }; @@ -855,6 +854,7 @@ export class CollectionFreeFormView extends CollectionSubView BoolCast(this.Document.useClusters); @computed get backgroundActive() { return this.layoutDoc.isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); } parentActive = () => this.props.active() || this.backgroundActive ? true : false; @@ -873,6 +873,7 @@ export class CollectionFreeFormView extends CollectionSubView { @@ -229,6 +230,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu rootSelected={this.rootSelected} dropAction={StrCast(this.props.Document.childDropAction) as dropActionType} onClick={this.onChildClickHandler} + onDoubleClick={this.onChildDoubleClickHandler} getTransform={dxf} focus={this.props.focus} CollectionDoc={this.props.CollectionView?.props.Document} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index af0cc3b5c..615efdb39 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -203,7 +203,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); } - + @computed get onChildDoubleClickHandler() { return ScriptCast(this.Document.onChildDoubleClick); } addDocTab = (doc: Doc, where: string) => { if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) { @@ -229,6 +229,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) rootSelected={this.rootSelected} dropAction={StrCast(this.props.Document.childDropAction) as dropActionType} onClick={this.onChildClickHandler} + onDoubleClick={this.onChildDoubleClickHandler} getTransform={dxf} focus={this.props.focus} CollectionDoc={this.props.CollectionView?.props.Document} diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx index 641797cac..d0b0c8ee6 100644 --- a/src/client/views/nodes/ContentFittingDocumentView.tsx +++ b/src/client/views/nodes/ContentFittingDocumentView.tsx @@ -32,6 +32,7 @@ interface ContentFittingDocumentViewProps { CollectionView?: CollectionView; CollectionDoc?: Doc; onClick?: ScriptField; + onDoubleClick?: ScriptField; backgroundColor?: (doc: Doc) => string | undefined; getTransform: () => Transform; addDocument?: (document: Doc) => boolean; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index fdcaa2df3..7b28a45f8 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -64,6 +64,7 @@ export interface DocumentViewProps { contextMenuItems?: () => { script: ScriptField, label: string }[]; rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected onClick?: ScriptField; + onDoubleClick?: ScriptField; onPointerDown?: ScriptField; onPointerUp?: ScriptField; dropAction?: dropActionType; @@ -116,6 +117,7 @@ export class DocumentView extends DocComponent(Docu @computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); } @computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight, this.props.NativeHeight() || (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); } @computed get onClickHandler() { return this.props.onClick || Cast(this.layoutDoc.onClick, ScriptField, null) || this.Document.onClick; } + @computed get onDoubleClickHandler() { return this.props.onDoubleClick || Cast(this.layoutDoc.onDoubleClick, ScriptField, null) || this.Document.onDoubleClick; } @computed get onPointerDownHandler() { return this.props.onPointerDown ? this.props.onPointerDown : this.Document.onPointerDown; } @computed get onPointerUpHandler() { return this.props.onPointerUp ? this.props.onPointerUp : this.Document.onPointerUp; } NativeWidth = () => this.nativeWidth; @@ -289,13 +291,22 @@ export class DocumentView extends DocComponent(Docu !this.props.Document.isBackground && this.props.bringToFront(this.props.Document); if (this._doubleTap && this.props.renderDepth && !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 if (!(e.nativeEvent as any).formattedHandled) { - const fullScreenAlias = Doc.MakeAlias(this.props.Document); - if (StrCast(fullScreenAlias.layoutKey) !== "layout_fullScreen" && fullScreenAlias.layout_fullScreen) { - fullScreenAlias.layoutKey = "layout_fullScreen"; + if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself + const func = () => this.onDoubleClickHandler.script.run({ + this: this.layoutDoc, + self: this.rootDoc, + thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey + }, console.log); + func(); + } else { + const fullScreenAlias = Doc.MakeAlias(this.props.Document); + if (StrCast(fullScreenAlias.layoutKey) !== "layout_fullScreen" && fullScreenAlias.layout_fullScreen) { + fullScreenAlias.layoutKey = "layout_fullScreen"; + } + UndoManager.RunInBatch(() => this.props.addDocTab(fullScreenAlias, "inTab"), "double tap"); + SelectionManager.DeselectAll(); + Doc.UnBrushDoc(this.props.Document); } - UndoManager.RunInBatch(() => this.props.addDocTab(fullScreenAlias, "inTab"), "double tap"); - SelectionManager.DeselectAll(); - Doc.UnBrushDoc(this.props.Document); } } else if (this.onClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself //SelectionManager.DeselectAll(); diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts index aa44cefa0..3d784448d 100644 --- a/src/new_fields/Types.ts +++ b/src/new_fields/Types.ts @@ -88,8 +88,8 @@ export function DateCast(field: FieldResult) { return Cast(field, DateField, null); } -export function ScriptCast(field: FieldResult) { - return Cast(field, ScriptField, null); +export function ScriptCast(field: FieldResult, defaultVal: ScriptField | null = null) { + return Cast(field, ScriptField, defaultVal); } type WithoutList = T extends List ? (R extends RefField ? (R | Promise)[] : R[]) : T; diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index 7a0be8863..5ca0d681e 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -81,6 +81,7 @@ export const collectionSchema = createSchema({ childLayout: Doc, // layout template for children of a collecion childDetailView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to use this field) onChildClick: ScriptField, // script to run for each child when its clicked + onChildDoubleClick: ScriptField, // script to run for each child when its clicked onCheckedClick: ScriptField, // script to run when a checkbox is clicked next to a child in a tree view }); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index eedd3ee67..1be977e4f 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -594,9 +594,13 @@ export class CurrentUserUtils { if (doc.childClickFuncs === undefined) { const openInTarget = Docs.Create.ScriptingDocument(ScriptField.MakeScript( "docCast(thisContainer.target).then((target) => { target && docCast(this.source).then((source) => { target.proto.data = new List([source || this]); } ); } )", - { target: Doc.name }), { title: "On Child Clicked (open in target)", _width: 300, _height: 200 }); + { target: Doc.name }), { title: "On Child Clicked (open in target)", _width: 300, _height: 200, scriptKey: "onChildClick" }); - doc.childClickFuncs = Docs.Create.TreeDocument([openInTarget], { title: "on Child Click function templates" }); + const openDetail = Docs.Create.ScriptingDocument(ScriptField.MakeScript( + "openOnRight(self.doubleClickView)", + { target: Doc.name }), { title: "On Child Dbl Clicked (open double click view)", _width: 300, _height: 200, scriptKey: "onChildDoubleClick" }); + + doc.childClickFuncs = Docs.Create.TreeDocument([openInTarget, openDetail], { title: "on Child Click function templates" }); } // this is equivalent to using PrefetchProxies to make sure all the childClickFuncs have been retrieved. PromiseValue(Cast(doc.childClickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast)); -- cgit v1.2.3-70-g09d2 From d4dd4ccd299ba2e06b35a6f14f698a26d026aeee Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 30 Apr 2020 11:34:08 -0400 Subject: clean up of childclick template func menus --- src/client/documents/Documents.ts | 2 +- src/client/views/collections/CollectionView.tsx | 16 ++++++++++------ src/server/authentication/models/current_user_utils.ts | 10 +++++++--- 3 files changed, 18 insertions(+), 10 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 436e59daf..0809ae24f 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -95,7 +95,7 @@ export interface DocumentOptions { hideHeadings?: boolean; // whether stacking view column headings should be hidden isTemplateForField?: string; // the field key for which the containing document is a rendering template isTemplateDoc?: boolean; - scriptKey?: string; // the script key that a child click func script template document will write into + targetScriptKey?: string; // where to write a template script (used by collections with click templates which need to target onClick, onDoubleClick, etc) templates?: List; backgroundColor?: string | ScriptField; // background color for data doc _backgroundColor?: string | ScriptField; // background color for each template layout doc ( overrides backgroundColor ) diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 2c52097aa..79bb6c41b 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -247,16 +247,20 @@ export class CollectionView extends Touchable { const existingOnClick = ContextMenu.Instance.findByDescription("OnClick..."); const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; - const funcs = [{ key: "onChildClick", name: "On Child Clicked", script: undefined as any as ScriptField }, - { key: "onChildDoubleClick", name: "On Child Double Clicked", script: undefined as any as ScriptField }]; - DocListCast(Cast(Doc.UserDoc().childClickFuncs, Doc, null).data).forEach(childClick => - funcs.push({ key: StrCast(childClick.scriptKey), name: StrCast(childClick.title), script: ScriptCast(childClick.data) })); + const funcs = [ + { key: "onChildClick", name: "On Child Clicked" }, + { key: "onChildDoubleClick", name: "On Child Double Clicked" }]; funcs.map(func => onClicks.push({ description: `Edit ${func.name} script`, icon: "edit", event: (obj: any) => { - func.script && (this.props.Document[func.key] = ObjectField.MakeCopy(func.script)); - ScriptBox.EditButtonScript(func.name + "...", this.props.Document, func.key, obj.x, obj.y, { thisContainer: Doc.name }, func.script); + ScriptBox.EditButtonScript(func.name + "...", this.props.Document, func.key, obj.x, obj.y, { thisContainer: Doc.name }); } })); + DocListCast(Cast(Doc.UserDoc().childClickFuncs, Doc, null).data).forEach(childClick => + onClicks.push({ + description: `Set child ${childClick.title}`, + icon: "edit", + event: () => this.props.Document[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)), + })); !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" }); const more = ContextMenu.Instance.findByDescription("More..."); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 1be977e4f..2c861d4fa 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -593,12 +593,16 @@ export class CurrentUserUtils { static setupClickEditorTemplates(doc: Doc) { if (doc.childClickFuncs === undefined) { const openInTarget = Docs.Create.ScriptingDocument(ScriptField.MakeScript( - "docCast(thisContainer.target).then((target) => { target && docCast(this.source).then((source) => { target.proto.data = new List([source || this]); } ); } )", - { target: Doc.name }), { title: "On Child Clicked (open in target)", _width: 300, _height: 200, scriptKey: "onChildClick" }); + "docCast(thisContainer.target).then((target) => {" + + " target && docCast(this.source).then((source) => { " + + " target.proto.data = new List([source || this]); " + + " }); " + + "})", + { target: Doc.name }), { title: "Click to open in target", _width: 300, _height: 200, targetScriptKey: "onChildClick" }); const openDetail = Docs.Create.ScriptingDocument(ScriptField.MakeScript( "openOnRight(self.doubleClickView)", - { target: Doc.name }), { title: "On Child Dbl Clicked (open double click view)", _width: 300, _height: 200, scriptKey: "onChildDoubleClick" }); + { target: Doc.name }), { title: "Double click to open doubleClickView", _width: 300, _height: 200, targetScriptKey: "onChildDoubleClick" }); doc.childClickFuncs = Docs.Create.TreeDocument([openInTarget, openDetail], { title: "on Child Click function templates" }); } -- cgit v1.2.3-70-g09d2 From 3c6162ce8f88fed8e9c84fd77423e858aa6cc7b4 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Thu, 30 Apr 2020 19:13:06 -0700 Subject: commented corrollary functions --- src/client/documents/Documents.ts | 24 +++++++++++++++++++++--- src/server/Websocket/Websocket.ts | 7 ++++++- 2 files changed, 27 insertions(+), 4 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 0809ae24f..2e81d5fa6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -379,6 +379,15 @@ export namespace Docs { */ export namespace Create { + /** + * Synchronously returns a collection into which + * the device documents will be put. This is initially empty, + * but gets populated by updates from the web socket. When everything is over, + * this function cleans up after itself. + * s + * Look at Websocket.ts for the server-side counterpart to this + * function. + */ export function Buxton() { let responded = false; const loading = new Doc; @@ -391,9 +400,13 @@ export namespace Docs { }); const parentProto = Doc.GetProto(parent); const { _socket } = DocServer; + + // just in case, clean up _socket.off(MessageStore.BuxtonDocumentResult.Message); _socket.off(MessageStore.BuxtonImportComplete.Message); - Utils.AddServerHandler(_socket, MessageStore.BuxtonDocumentResult, ({ device, errors }) => { + + // this is where the client handles the receipt of a new valid parsed document + Utils.AddServerHandler(_socket, MessageStore.BuxtonDocumentResult, ({ device, invalid: errors }) => { if (!responded) { responded = true; parentProto.data = new List(); @@ -408,9 +421,11 @@ export namespace Docs { _nativeWidth: nativeWidth, _nativeHeight: nativeHeight })); + // the main document we create const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true }); const deviceProto = Doc.GetProto(doc); deviceProto.hero = new ImageField(constructed[0].url); + // add the parsed attributes to this main document Docs.Get.FromJson({ data: device, appendToExisting: { targetDoc: deviceProto } }); Doc.AddDocToList(parentProto, "data", doc); } else if (errors) { @@ -419,14 +434,17 @@ export namespace Docs { alert("A Buxton document import was completely empty (??)"); } }); + + // when the import is complete, we stop listening for these creation + // and termination events and alert the user Utils.AddServerHandler(_socket, MessageStore.BuxtonImportComplete, ({ deviceCount, errorCount }) => { _socket.off(MessageStore.BuxtonDocumentResult.Message); _socket.off(MessageStore.BuxtonImportComplete.Message); alert(`Successfully imported ${deviceCount} device${deviceCount === 1 ? "" : "s"}, with ${errorCount} error${errorCount === 1 ? "" : "s"}, in ${(Date.now() - startTime) / 1000} seconds.`); }); const startTime = Date.now(); - Utils.Emit(_socket, MessageStore.BeginBuxtonImport, ""); - return parent; + Utils.Emit(_socket, MessageStore.BeginBuxtonImport, ""); // signal the server to start importing + return parent; // synchronously return the collection, to be populateds } Scripting.addGlobal(Buxton); diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts index 947aa4289..be895c4bc 100644 --- a/src/server/Websocket/Websocket.ts +++ b/src/server/Websocket/Websocket.ts @@ -10,7 +10,6 @@ import { GoogleCredentialsLoader } from "../credentials/CredentialsLoader"; import { logPort } from "../ActionUtilities"; import { timeMap } from "../ApiManagers/UserManager"; import { green } from "colors"; -import { serverPathToFile, Directory } from "../ApiManagers/UploadManager"; import { networkInterfaces } from "os"; import executeImport from "../../scraping/buxton/final/BuxtonImporter"; @@ -111,6 +110,12 @@ export namespace WebSocket { Utils.AddServerHandler(socket, MessageStore.MobileDocumentUpload, content => processMobileDocumentUpload(socket, content)); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields); + + /** + * Whenever we receive the go-ahead, invoke the import script and pass in + * as an emitter and a terminator the functions that simply broadcast a result + * or indicate termination to the client via the web socket + */ Utils.AddServerHandler(socket, MessageStore.BeginBuxtonImport, () => { executeImport( deviceOrError => Utils.Emit(socket, MessageStore.BuxtonDocumentResult, deviceOrError), -- cgit v1.2.3-70-g09d2 From ff7c7d40b1fcdf74b539c7d97f36707ff1521d2e Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 1 May 2020 01:46:07 -0400 Subject: fixed presentations to allow drag and drop. fixed pres box to use RenderData instead of modifying presentation elements with unnecessary info like their containing PresBox and their presentation index position. COnverted COntentFIttingDocumentView to use DocumentView's props --- src/client/documents/Documents.ts | 1 + src/client/views/MainView.tsx | 4 +- src/client/views/SearchDocBox.tsx | 10 ++++- .../views/collections/CollectionCarouselView.tsx | 8 +++- .../views/collections/CollectionSchemaView.tsx | 14 ++++--- .../views/collections/CollectionStackingView.tsx | 15 ++++--- .../views/collections/CollectionTreeView.tsx | 20 +++++---- src/client/views/collections/CollectionView.tsx | 14 ++++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 3 +- .../CollectionMulticolumnView.tsx | 14 ++++--- .../CollectionMultirowView.tsx | 14 ++++--- .../views/nodes/CollectionFreeFormDocumentView.tsx | 8 ++-- .../views/nodes/ContentFittingDocumentView.tsx | 49 ++++------------------ src/client/views/nodes/DocumentBox.tsx | 15 ++++--- src/client/views/nodes/DocumentView.tsx | 20 ++++----- src/client/views/nodes/FieldView.tsx | 1 + src/client/views/nodes/PresBox.tsx | 21 ++++------ .../formattedText/FormattedTextBoxComment.tsx | 12 ++++-- .../views/nodes/formattedText/RichTextMenu.scss | 3 ++ .../views/presentationview/PresElementBox.tsx | 25 +++++++---- src/client/views/search/SearchItem.tsx | 12 ++++-- src/new_fields/documentSchemas.ts | 2 +- .../authentication/models/current_user_utils.ts | 2 +- 23 files changed, 150 insertions(+), 137 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2e81d5fa6..228a6af97 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -81,6 +81,7 @@ export interface DocumentOptions { author?: string; dropAction?: dropActionType; childDropAction?: dropActionType; + targetDropAction?: dropActionType; layoutKey?: string; type?: string; title?: string; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 65e4eb036..a29a6baac 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -597,8 +597,8 @@ export class MainView extends React.Component { {// TO VIEW SNAP LINES
- {this._hLines?.map(l => )} - {this._vLines?.map(l => )} + {this._hLines?.map((l: any) => )} + {this._vLines?.map((l: any) => )}
} diff --git a/src/client/views/SearchDocBox.tsx b/src/client/views/SearchDocBox.tsx index 799fa9d85..7bd689b19 100644 --- a/src/client/views/SearchDocBox.tsx +++ b/src/client/views/SearchDocBox.tsx @@ -6,7 +6,7 @@ import { observer } from "mobx-react"; import { Doc, DocListCast } from "../../new_fields/Doc"; import { Id } from "../../new_fields/FieldSymbols"; import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types"; -import { returnFalse } from "../../Utils"; +import { returnFalse, returnZero } from "../../Utils"; import { Docs } from "../documents/Documents"; import { SearchUtil } from "../util/SearchUtil"; import { EditableView } from "./EditableView"; @@ -399,7 +399,13 @@ export class SearchDocBox extends React.Component { + bringToFront={returnFalse} + ContainingCollectionDoc={undefined} + ContainingCollectionView={undefined} + NativeWidth={returnZero} + NativeHeight={returnZero} + parentActive={this.props.active} + ScreenToLocalTransform={this.props.ScreenToLocalTransform}>
; const CarouselDocument = makeInterface(documentSchema); @@ -49,9 +50,12 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument) + ScreenToLocalTransform={this.props.ScreenToLocalTransform} + bringToFront={returnFalse} + parentActive={this.props.active} + />
doc) { {!this.previewDocument ? (null) : doc) { rootSelected={this.rootSelected} PanelWidth={this.previewWidth} PanelHeight={this.previewHeight} - getTransform={this.getPreviewTransform} - CollectionDoc={this.props.CollectionView?.props.Document} - CollectionView={this.props.CollectionView} + ScreenToLocalTransform={this.getPreviewTransform} + ContainingCollectionDoc={this.props.CollectionView?.props.Document} + ContainingCollectionView={this.props.CollectionView} moveDocument={this.props.moveDocument} addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} - active={this.props.active} + parentActive={this.props.active} whenActiveChanged={this.props.whenActiveChanged} addDocTab={this.props.addDocTab} pinToPres={this.props.pinToPres} + bringToFront={returnFalse} + ContentScaling={returnOne} />}
; } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 556d7df5c..6c230d5b1 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -11,7 +11,7 @@ import { listSpec } from "../../../new_fields/Schema"; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types"; import { TraceMobx } from "../../../new_fields/util"; -import { Utils, setupMoveUpEvents, emptyFunction, returnZero, returnOne } from "../../../Utils"; +import { Utils, setupMoveUpEvents, emptyFunction, returnZero, returnOne, returnFalse } from "../../../Utils"; import { DragManager, dropActionType } from "../../util/DragManager"; import { Transform } from "../../util/Transform"; import { undoBatch } from "../../util/UndoManager"; @@ -165,12 +165,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { const height = () => this.getDocHeight(doc); return doc) { dropAction={StrCast(this.props.Document.childDropAction) as dropActionType} onClick={this.onChildClickHandler} onDoubleClick={this.onChildDoubleClickHandler} - getTransform={dxf} + ScreenToLocalTransform={dxf} focus={this.props.focus} - CollectionDoc={this.props.CollectionView?.props.Document} - CollectionView={this.props.CollectionView} + ContainingCollectionDoc={this.props.CollectionView?.props.Document} + ContainingCollectionView={this.props.CollectionView} addDocument={this.props.addDocument} moveDocument={this.props.moveDocument} removeDocument={this.props.removeDocument} - active={this.props.active} + parentActive={this.props.active} whenActiveChanged={this.props.whenActiveChanged} addDocTab={this.addDocTab} + bringToFront={returnFalse} + ContentScaling={returnOne} pinToPres={this.props.pinToPres} />; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index d938bd7ad..71358a8ec 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -353,27 +353,31 @@ class TreeView extends React.Component { return
+ pinToPres={this.props.pinToPres} + bringToFront={returnFalse} + ContentScaling={returnOne} + />
; } } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 8d8c321e8..561226de5 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -113,12 +113,16 @@ export class CollectionView extends Touchable { @action.bound addDocument(doc: Doc): boolean { - const targetDataDoc = this.props.Document[DataSym]; - const docList = DocListCast(targetDataDoc[this.props.fieldKey]); - !docList.includes(doc) && (targetDataDoc[this.props.fieldKey] = new List([...docList, doc])); // DocAddToList may write to targetdataDoc's parent ... we don't want this. should really change GetProto to GetDataDoc and test for resolvedDataDoc there - // Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc); + if (this.props.addDocument) { + this.props.addDocument(doc); + } else { + const targetDataDoc = this.props.Document[DataSym]; + const docList = DocListCast(targetDataDoc[this.props.fieldKey]); + !docList.includes(doc) && (targetDataDoc[this.props.fieldKey] = new List([...docList, doc])); // DocAddToList may write to targetdataDoc's parent ... we don't want this. should really change GetProto to GetDataDoc and test for resolvedDataDoc there + // Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc); + targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); + } doc.context = this.props.Document; - targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); Doc.GetProto(doc).lastOpened = new DateField; return true; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 763a6c605..b4eb22444 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -877,6 +877,7 @@ export class CollectionFreeFormView extends CollectionSubView { if (SelectionManager.GetIsDragging()) { - this.setupDragLines(e); + this.setupDragLines(); } e.stopPropagation(); } diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 66d441115..b3a6a9deb 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -14,7 +14,7 @@ import "./collectionMulticolumnView.scss"; import ResizeBar from './MulticolumnResizer'; import WidthLabel from './MulticolumnWidthLabel'; import { List } from '../../../../new_fields/List'; -import { returnZero } from '../../../../Utils'; +import { returnZero, returnFalse, returnOne } from '../../../../Utils'; type MulticolumnDocument = makeInterface<[typeof documentSchema]>; const MulticolumnDocument = makeInterface(documentSchema); @@ -216,7 +216,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { return ; } /** diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 615efdb39..0fb29ca61 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -6,7 +6,7 @@ import * as React from "react"; import { Doc } from '../../../../new_fields/Doc'; import { NumCast, StrCast, BoolCast, ScriptCast } from '../../../../new_fields/Types'; import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView'; -import { Utils, returnZero } from '../../../../Utils'; +import { Utils, returnZero, returnFalse, returnOne } from '../../../../Utils'; import "./collectionMultirowView.scss"; import { computed, trace, observable, action } from 'mobx'; import { Transform } from '../../../util/Transform'; @@ -215,7 +215,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { return ; } /** diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 1c7d116c5..24468dcc1 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -115,13 +115,11 @@ export class CollectionFreeFormDocumentView extends DocComponent : } diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx index d0b0c8ee6..637fd5acc 100644 --- a/src/client/views/nodes/ContentFittingDocumentView.tsx +++ b/src/client/views/nodes/ContentFittingDocumentView.tsx @@ -3,51 +3,16 @@ import { computed } from "mobx"; import { observer } from "mobx-react"; import "react-table/react-table.css"; import { Doc, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc"; -import { ScriptField } from "../../../new_fields/ScriptField"; import { NumCast, StrCast } from "../../../new_fields/Types"; import { TraceMobx } from "../../../new_fields/util"; import { emptyFunction, returnOne } from "../../../Utils"; -import { Transform } from "../../util/Transform"; -import { CollectionView } from "../collections/CollectionView"; import '../DocumentDecorations.scss'; -import { DocumentView } from "../nodes/DocumentView"; +import { DocumentView, DocumentViewProps } from "../nodes/DocumentView"; import "./ContentFittingDocumentView.scss"; -import { dropActionType } from "../../util/DragManager"; -interface ContentFittingDocumentViewProps { - Document: Doc; - DataDocument?: Doc; - LayoutDoc?: () => Opt; - NativeWidth?: () => number; - NativeHeight?: () => number; - FreezeDimensions?: boolean; - LibraryPath: Doc[]; - renderDepth: number; - fitToBox?: boolean; - layoutKey?: string; - dropAction?: dropActionType; - PanelWidth: () => number; - PanelHeight: () => number; - focus?: (doc: Doc) => void; - CollectionView?: CollectionView; - CollectionDoc?: Doc; - onClick?: ScriptField; - onDoubleClick?: ScriptField; - backgroundColor?: (doc: Doc) => string | undefined; - getTransform: () => Transform; - addDocument?: (document: Doc) => boolean; - moveDocument?: (document: Doc, target: Doc | undefined, addDoc: ((doc: Doc) => boolean)) => boolean; - removeDocument?: (document: Doc) => boolean; - active: (outsideReaction: boolean) => boolean; - whenActiveChanged: (isActive: boolean) => void; - addDocTab: (document: Doc, where: string) => boolean; - pinToPres: (document: Doc) => void; - dontRegisterView?: boolean; - rootSelected: (outsideReaction?: boolean) => boolean; -} @observer -export class ContentFittingDocumentView extends React.Component{ +export class ContentFittingDocumentView extends React.Component{ public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive private get layoutDoc() { return this.props.LayoutDoc?.() || Doc.Layout(this.props.Document); } @computed get freezeDimensions() { return this.props.FreezeDimensions; } @@ -68,7 +33,7 @@ export class ContentFittingDocumentView extends React.Component this.props.getTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(1 / this.contentScaling()); + private getTransform = () => this.props.ScreenToLocalTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(1 / this.contentScaling()); private get centeringOffset() { return this.nativeWidth() && !this.props.Document._fitWidth ? (this.props.PanelWidth() - this.nativeWidth() * this.contentScaling()) / 2 : 0; } private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 ? (this.props.PanelHeight() - this.nativeHeight() * this.contentScaling()) / 2 : 0; } @@ -90,7 +55,7 @@ export class ContentFittingDocumentView extends React.Component ; return contents; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 085637440..f555d6eef 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -77,6 +77,7 @@ export interface DocumentViewProps { setupDragLines?: () => void; renderDepth: number; ContentScaling: () => number; + RenderData?: () => Doc; PanelWidth: () => number; PanelHeight: () => number; pointerEvents?: boolean; @@ -992,6 +993,7 @@ export class DocumentView extends DocComponent(Docu LayoutDoc={this.props.LayoutDoc} makeLink={this.makeLink} rootSelected={this.rootSelected} + RenderData={this.props.RenderData} dontRegisterView={this.props.dontRegisterView} fitToBox={this.props.fitToBox} LibraryPath={this.props.LibraryPath} @@ -1112,17 +1114,13 @@ export class DocumentView extends DocComponent(Docu Doc.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined); } } - @observable _animate = 0; + @observable _animateScalingTo = 0; switchViews = action((custom: boolean, view: string) => { - SelectionManager.SetIsDragging(true); - this._animate = 0.1; + this._animateScalingTo = 0.1; // shrink doc setTimeout(action(() => { this.setCustomView(custom, view); - this._animate = 1; - setTimeout(action(() => { - this._animate = 0; - SelectionManager.SetIsDragging(false); - }), 400); + this._animateScalingTo = 1; // expand it + setTimeout(action(() => this._animateScalingTo = 0), 400); }), 400); }); @@ -1156,9 +1154,9 @@ export class DocumentView extends DocComponent(Docu !entered && Doc.UnBrushDoc(this.props.Document); })} style={{ - transformOrigin: this._animate ? "center center" : undefined, - transform: this._animate ? `scale(${this._animate})` : undefined, - transition: !this._animate ? StrCast(this.Document.transition) : this._animate < 1 ? "transform 0.5s ease-in" : "transform 0.5s ease-out", + transformOrigin: this._animateScalingTo ? "center center" : undefined, + transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined, + transition: !this._animateScalingTo ? StrCast(this.Document.transition) : this._animateScalingTo < 1 ? "transform 0.5s ease-in" : "transform 0.5s ease-out", pointerEvents: this.ignorePointerEvents ? "none" : undefined, color: StrCast(this.layoutDoc.color, "inherit"), outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px", diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 0b9edbcd3..1efee4f5a 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -56,6 +56,7 @@ export interface FieldViewProps { width?: number; background?: string; color?: string; + RenderData?: () => Doc; } @observer diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index f91a809bb..3fcc97473 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -16,6 +16,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import "./PresBox.scss"; import { ViewBoxBaseComponent } from "../DocComponent"; import { makeInterface } from "../../../new_fields/Schema"; +import { List } from "../../../new_fields/List"; type PresBoxSchema = makeInterface<[typeof documentSchema]>; const PresBoxDocument = makeInterface(documentSchema); @@ -23,20 +24,15 @@ const PresBoxDocument = makeInterface(documentSchema); @observer export class PresBox extends ViewBoxBaseComponent(PresBoxDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); } - private _childReaction: IReactionDisposer | undefined; @observable _isChildActive = false; @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); } @computed get currentIndex() { return NumCast(this.rootDoc._itemIndex); } componentDidMount() { + this.rootDoc.presBox = this.rootDoc; this.rootDoc._forceRenderEngine = "timeline"; this.rootDoc._replacedChrome = "replaced"; - this._childReaction = reaction(() => this.childDocs.slice(), (children) => children.forEach((child, i) => child.presentationIndex = i), { fireImmediately: true }); } - componentWillUnmount() { - this._childReaction?.(); - } - updateCurrentPresentation = () => Doc.UserDoc().activePresentation = this.rootDoc; @undoBatch @@ -247,16 +243,12 @@ export class PresBox extends ViewBoxBaseComponent initializeViewAliases = (docList: Doc[], viewtype: CollectionViewType) => { const hgt = (viewtype === CollectionViewType.Tree) ? 50 : 46; - docList.forEach(doc => { - doc.presBox = this.rootDoc; // give contained documents a reference to the presentation - doc.presCollapsedHeight = hgt; // set the collpased height for documents based on the type of view (Tree or Stack) they will be displaye din - }); + this.rootDoc.presCollapsedHeight = hgt; } addDocument = (doc: Doc) => { - const newPinDoc = Doc.MakeAlias(doc); - newPinDoc.presentationTargetDoc = doc; - return Doc.AddDocToList(this.dataDoc, this.fieldKey, newPinDoc); + doc.presentationTargetDoc = doc.aliasOf; + return Doc.AddDocToList(this.dataDoc, this.fieldKey, doc); } removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc); @@ -280,8 +272,10 @@ export class PresBox extends ViewBoxBaseComponent this.updateMinimize(e, this.rootDoc._viewType = viewType); }); + returnSelf = () => this.rootDoc; childLayoutTemplate = () => this.rootDoc._viewType === CollectionViewType.Stacking ? Cast(Doc.UserDoc()["template-presentation"], Doc, null) : undefined; render() { + this.rootDoc.presOrderedDocs = new List(this.childDocs.map((child, i) => child)); const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; this.initializeViewAliases(this.childDocs, mode); return
@@ -314,6 +308,7 @@ export class PresBox extends ViewBoxBaseComponent childLayoutTemplate={this.childLayoutTemplate} addDocument={this.addDocument} removeDocument={returnFalse} + RenderData={this.returnSelf} focus={this.selectElement} ScreenToLocalTransform={this.getTransform} /> : (null) diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index f9e4c5210..9ad5aafb8 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -4,7 +4,7 @@ import { EditorView } from "prosemirror-view"; import * as ReactDOM from 'react-dom'; import { Doc, DocCastAsync } from "../../../../new_fields/Doc"; import { Cast, FieldValue, NumCast } from "../../../../new_fields/Types"; -import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath } from "../../../../Utils"; +import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; import { DocumentManager } from "../../../util/DocumentManager"; import { schema } from "./schema_rts"; @@ -192,18 +192,24 @@ export class FormattedTextBoxComment { fitToBox={true} moveDocument={returnFalse} rootSelected={returnFalse} - getTransform={Transform.Identity} - active={returnFalse} + ScreenToLocalTransform={Transform.Identity} + parentActive={returnFalse} addDocument={returnFalse} removeDocument={returnFalse} addDocTab={returnFalse} pinToPres={returnFalse} dontRegisterView={true} + ContainingCollectionDoc={undefined} + ContainingCollectionView={undefined} renderDepth={1} PanelWidth={() => Math.min(350, NumCast(target._width, 350))} PanelHeight={() => Math.min(250, NumCast(target._height, 250))} focus={emptyFunction} whenActiveChanged={returnFalse} + bringToFront={returnFalse} + ContentScaling={returnOne} + NativeWidth={returnZero} + NativeHeight={returnZero} />, FormattedTextBoxComment.tooltipText); FormattedTextBoxComment.tooltip.style.width = NumCast(target.width) ? `${NumCast(target.width)}` : "100%"; FormattedTextBoxComment.tooltip.style.height = NumCast(target.height) ? `${NumCast(target.height)}` : "100%"; diff --git a/src/client/views/nodes/formattedText/RichTextMenu.scss b/src/client/views/nodes/formattedText/RichTextMenu.scss index 3a16171de..7a0718c16 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.scss +++ b/src/client/views/nodes/formattedText/RichTextMenu.scss @@ -55,6 +55,9 @@ color: black; } +} + +.richTextMenu { select { background-color: #323232; color: white; diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index 66f251b93..1887c8d45 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -1,12 +1,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DataSym } from "../../../new_fields/Doc"; +import { Doc, DataSym, DocListCast } from "../../../new_fields/Doc"; import { documentSchema } from '../../../new_fields/documentSchemas'; import { Id } from "../../../new_fields/FieldSymbols"; import { createSchema, makeInterface } from '../../../new_fields/Schema'; import { Cast, NumCast } from "../../../new_fields/Types"; -import { emptyFunction, emptyPath, returnFalse, returnTrue } from "../../../Utils"; +import { emptyFunction, emptyPath, returnFalse, returnTrue, returnOne, returnZero } from "../../../Utils"; import { Transform } from "../../util/Transform"; import { CollectionViewType } from '../collections/CollectionView'; import { ViewBoxBaseComponent } from '../DocComponent'; @@ -38,13 +38,14 @@ export class PresElementBox extends ViewBoxBaseComponent d === this.rootDoc); } + @computed get presBoxDoc() { return Cast(this.props.RenderData?.().presBox, Doc) as Doc; } @computed get targetDoc() { return this.rootDoc.presentationTargetDoc as Doc; } @computed get currentIndex() { return NumCast(this.presBoxDoc?._itemIndex); } + @computed get collapsedHeight() { return NumCast(this.presBoxDoc?.presCollapsedHeight); } componentDidMount() { - this._heightDisposer = reaction(() => [this.rootDoc.presExpandInlineButton, this.rootDoc.presCollapsedHeight], + this._heightDisposer = reaction(() => [this.rootDoc.presExpandInlineButton, this.collapsedHeight], params => this.layoutDoc._height = NumCast(params[1]) + (Number(params[0]) ? 100 : 0), { fireImmediately: true }); } componentWillUnmount() { @@ -147,7 +148,7 @@ export class PresElementBox extends ViewBoxBaseComponent [xCord, yCord]; - embedHeight = () => Math.min(this.props.PanelWidth() - 20, this.props.PanelHeight() - NumCast(this.rootDoc.presCollapsedHeight)); + embedHeight = () => Math.min(this.props.PanelWidth() - 20, this.props.PanelHeight() - this.collapsedHeight); embedWidth = () => this.props.PanelWidth() - 20; /** * The function that is responsible for rendering the a preview or not for this @@ -158,7 +159,7 @@ export class PresElementBox extends ViewBoxBaseComponent
; diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index fe2000700..96f43e931 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -7,7 +7,7 @@ import { observer } from "mobx-react"; import { Doc } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { emptyFunction, emptyPath, returnFalse, Utils, returnTrue } from "../../../Utils"; +import { emptyFunction, emptyPath, returnFalse, Utils, returnTrue, returnOne, returnZero } from "../../../Utils"; import { DocumentType } from "../../documents/DocumentTypes"; import { DocumentManager } from "../../util/DocumentManager"; import { DragManager, SetupDrag } from "../../util/DragManager"; @@ -164,14 +164,20 @@ export class SearchItem extends React.Component { removeDocument={returnFalse} addDocTab={returnFalse} pinToPres={returnFalse} - getTransform={Transform.Identity} + ContainingCollectionDoc={undefined} + ContainingCollectionView={undefined} + ScreenToLocalTransform={Transform.Identity} renderDepth={1} PanelWidth={returnXDimension} PanelHeight={returnYDimension} + NativeWidth={returnZero} + NativeHeight={returnZero} focus={emptyFunction} moveDocument={returnFalse} - active={returnFalse} + parentActive={returnFalse} whenActiveChanged={returnFalse} + bringToFront={returnFalse} + ContentScaling={returnOne} />
; return docview; diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index 5ca0d681e..cd4b9d591 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -9,7 +9,7 @@ export const documentSchema = createSchema({ layoutKey: "string", // holds the field key for the field that actually holds the current lyoat title: "string", // document title (can be on either data document or layout) dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias", "copy", "move") - targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") + targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") childDropAction: "string", // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy") _autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents _nativeWidth: "number", // native width of document which determines how much document contents are scaled when the document's width is set diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index d7cc1e6bf..f37538252 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -578,7 +578,7 @@ export class CurrentUserUtils { } if (doc.activePresentation === undefined) { doc.activePresentation = Docs.Create.PresDocument(new List(), { - title: "Presentation", _viewType: CollectionViewType.Stacking, + title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias", _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" }); } -- cgit v1.2.3-70-g09d2 From 37324d134c8d788a73b8b1d35afa7ea6b0e88d37 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 1 May 2020 12:47:51 -0400 Subject: cleaned up buxton template. fixed adding docs to collections. --- src/client/documents/Documents.ts | 7 +++---- src/client/views/collections/CollectionPileView.tsx | 4 ++-- .../views/collections/CollectionStackingView.scss | 7 +++++-- .../CollectionStackingViewFieldColumn.tsx | 6 +----- src/client/views/collections/CollectionView.tsx | 21 ++++++++++++--------- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/PresBox.tsx | 6 +++--- src/new_fields/Doc.ts | 2 +- src/new_fields/documentSchemas.ts | 4 ++-- .../authentication/models/current_user_utils.ts | 7 ++++--- 11 files changed, 35 insertions(+), 33 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 228a6af97..3d790a485 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -98,6 +98,7 @@ export interface DocumentOptions { isTemplateDoc?: boolean; targetScriptKey?: string; // where to write a template script (used by collections with click templates which need to target onClick, onDoubleClick, etc) templates?: List; + hero?: ImageField; // primary image that best represents a compound document (e.g., for a buxton device document that has multiple images) backgroundColor?: string | ScriptField; // background color for data doc _backgroundColor?: string | ScriptField; // background color for each template layout doc ( overrides backgroundColor ) color?: string; // foreground color data doc @@ -423,11 +424,9 @@ export namespace Docs { _nativeHeight: nativeHeight })); // the main document we create - const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true }); - const deviceProto = Doc.GetProto(doc); - deviceProto.hero = new ImageField(constructed[0].url); + const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true, hero: new ImageField(constructed[0].url) }); // add the parsed attributes to this main document - Docs.Get.FromJson({ data: device, appendToExisting: { targetDoc: deviceProto } }); + Docs.Get.FromJson({ data: device, appendToExisting: { targetDoc: Doc.GetProto(doc) } }); Doc.AddDocToList(parentProto, "data", doc); } else if (errors) { console.log(errors); diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index 3bbfcc4d7..8b8cbc6e8 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -53,7 +53,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { toggleStarburst = action(() => { if (this._layoutEngine === 'starburst') { const defaultSize = 110; - this.layoutDoc.overflow = undefined; + this.layoutDoc._overflow = undefined; this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - NumCast(this.layoutDoc._starburstPileWidth, defaultSize) / 2; this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2; this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize); @@ -61,7 +61,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { this._layoutEngine = 'pass'; } else { const defaultSize = 25; - this.layoutDoc.overflow = 'visible'; + this.layoutDoc._overflow = 'visible'; !this.layoutDoc._starburstRadius && (this.layoutDoc._starburstRadius = 500); !this.layoutDoc._starburstDocScale && (this.layoutDoc._starburstDocScale = 2.5); if (this._layoutEngine === 'pass') { diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss index 47faa9239..5eaf29316 100644 --- a/src/client/views/collections/CollectionStackingView.scss +++ b/src/client/views/collections/CollectionStackingView.scss @@ -180,13 +180,16 @@ .collectionStackingView-sectionHeader-subCont { outline: none; border: 0px; - color: $light-color; width: 100%; - color: grey; letter-spacing: 2px; font-size: 75%; transition: transform 0.2s; position: relative; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + color: lightGray; .editableView-container-editing-oneLine, .editableView-container-editing { diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 323d7be25..8a9539eb0 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -322,11 +322,7 @@ export class CollectionStackingViewFieldColumn extends React.Component + style={{ background: evContents !== `NO ${key.toUpperCase()} VALUE` ? this._color : "inherit" }}> {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 561226de5..91018980c 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -71,6 +71,9 @@ export enum CollectionViewType { Map = "map", Pile = "pileup" } +export interface CollectionViewCustomProps { + filterAddDocument: (doc: Doc) => boolean; +} export interface CollectionRenderProps { addDocument: (document: Doc) => boolean; @@ -82,7 +85,7 @@ export interface CollectionRenderProps { } @observer -export class CollectionView extends Touchable { +export class CollectionView extends Touchable { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); } private _isChildActive = false; //TODO should this be observable? @@ -113,15 +116,15 @@ export class CollectionView extends Touchable { @action.bound addDocument(doc: Doc): boolean { - if (this.props.addDocument) { - this.props.addDocument(doc); - } else { - const targetDataDoc = this.props.Document[DataSym]; - const docList = DocListCast(targetDataDoc[this.props.fieldKey]); - !docList.includes(doc) && (targetDataDoc[this.props.fieldKey] = new List([...docList, doc])); // DocAddToList may write to targetdataDoc's parent ... we don't want this. should really change GetProto to GetDataDoc and test for resolvedDataDoc there - // Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc); - targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); + if (this.props.filterAddDocument?.(doc) === false) { + return false; } + + const targetDataDoc = this.props.Document[DataSym]; + const docList = DocListCast(targetDataDoc[this.props.fieldKey]); + !docList.includes(doc) && (targetDataDoc[this.props.fieldKey] = new List([...docList, doc])); // DocAddToList may write to targetdataDoc's parent ... we don't want this. should really change GetProto to GetDataDoc and test for resolvedDataDoc there + // Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc); + targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); doc.context = this.props.Document; Doc.GetProto(doc).lastOpened = new DateField; return true; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index c70301b2f..5bac075b4 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -613,7 +613,7 @@ export class MarqueeView extends React.Component e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}> {this._visible ? this.marqueeDiv : null} {this.props.children} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f555d6eef..f1efa48f4 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -666,7 +666,7 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action toggleBackground = (temporary: boolean): void => { - this.Document.overflow = temporary ? "visible" : "hidden"; + this.Document._overflow = temporary ? "visible" : "hidden"; this.Document.isBackground = !temporary ? !this.Document.isBackground : (this.Document.isBackground ? undefined : true); this.Document.isBackground && this.props.bringToFront(this.Document, true); } diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 3fcc97473..72bbc9e4b 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -246,9 +246,9 @@ export class PresBox extends ViewBoxBaseComponent this.rootDoc.presCollapsedHeight = hgt; } - addDocument = (doc: Doc) => { + addDocumentFilter = (doc: Doc) => { doc.presentationTargetDoc = doc.aliasOf; - return Doc.AddDocToList(this.dataDoc, this.fieldKey, doc); + return true; } removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc); @@ -306,7 +306,7 @@ export class PresBox extends ViewBoxBaseComponent PanelHeight={this.panelHeight} moveDocument={returnFalse} childLayoutTemplate={this.childLayoutTemplate} - addDocument={this.addDocument} + filterAddDocument={this.addDocumentFilter} removeDocument={returnFalse} RenderData={this.returnSelf} focus={this.selectElement} diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 77eee03ce..71325d94f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -997,7 +997,7 @@ export namespace Doc { //newCollection.borderRounding = "40px"; newCollection._jitterRotation = 10; newCollection._backgroundColor = "gray"; - newCollection.overflow = "visible"; + newCollection._overflow = "visible"; return newCollection; } diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index cd4b9d591..7bf1c03c8 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -20,6 +20,7 @@ export const documentSchema = createSchema({ _yPadding: "number", // pixels of padding on top/bottom of collectionfreeformview contents when fitToBox is set _xMargin: "number", // margin added on left/right of most documents to add separation from their container _yMargin: "number", // margin added on top/bottom of most documents to add separation from their container + _overflow: "string", // sets overflow behvavior for CollectionFreeForm views _showCaption: "string", // whether editable caption text is overlayed at the bottom of the document _showTitle: "string", // the fieldkey whose contents should be displayed at the top of the document _showTitleHover: "string", // the showTitle should be shown only on hover @@ -36,7 +37,6 @@ export const documentSchema = createSchema({ color: "string", // foreground color of document backgroundColor: "string", // background color of document opacity: "number", // opacity of document - overflow: "string", // sets overflow behvavior for CollectionFreeForm views creationDate: DateField, // when the document was created links: listSpec(Doc), // computed (readonly) list of links associated with this document onClick: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) @@ -46,7 +46,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. removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped isTemplateForField: "string",// when specifies a field key, then the containing document is a template that renders the specified field - isBackground: "boolean", // whether document is a background element and ignores input events (can only selet with marquee) + isBackground: "boolean", // whether document is a background element and ignores input events (can only select with marquee) treeViewOpen: "boolean", // flag denoting whether the documents sub-tree (contents) is visible or hidden treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document) diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index f37538252..fbb27f9b7 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -89,7 +89,7 @@ export class CurrentUserUtils { const carousel = CarouselDocument([], { title: "data", _height: 350, _itemIndex: 0, backgroundColor: "#9b9b9b3F" }); const details = TextDocument("", { title: "details", _height: 350, _autoHeight: true }); - const short = TextDocument("", { title: "shortDescription", treeViewOpen: true, treeViewExpandedView: "layout", _height: 50, _autoHeight: true }); + const short = TextDocument("", { title: "shortDescription", treeViewOpen: true, treeViewExpandedView: "layout", _height: 100, _autoHeight: true }); const long = TextDocument("", { title: "longDescription", treeViewOpen: false, treeViewExpandedView: "layout", _height: 350, _autoHeight: true }); const buxtonFieldKeys = ["year", "originalPrice", "degreesOfFreedom", "company", "attribute", "primaryKey", "secondaryKey", "dimensions"]; @@ -111,8 +111,9 @@ export class CurrentUserUtils { const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts }); descriptionWrapper.sectionHeaders = new List([ - new SchemaHeaderField("[Long Description]", "LemonChiffon", undefined, undefined, undefined, true), - new SchemaHeaderField("[Details]", "lightBlue", undefined, undefined, undefined, true), + new SchemaHeaderField("[A Short Description]", "dimGray", undefined, undefined, undefined, false), + new SchemaHeaderField("[Long Description]", "dimGray", undefined, undefined, undefined, true), + new SchemaHeaderField("[Details]", "dimGray", undefined, undefined, undefined, true), ]); const detailView = Docs.Create.StackingDocument([carousel, descriptionWrapper], { ...shared, ...detailViewOpts }); detailView.isTemplateDoc = makeTemplate(detailView); -- cgit v1.2.3-70-g09d2 From 72a838b6a6165c7fa3050d78dbf16bf2cca6b840 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 1 May 2020 13:26:25 -0400 Subject: buxton template tweaks --- src/client/documents/Documents.ts | 2 ++ src/client/views/collections/CollectionCarouselView.tsx | 4 ++-- src/server/authentication/models/current_user_utils.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3d790a485..732f9303b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -75,6 +75,8 @@ export interface DocumentOptions { _itemIndex?: number; // which item index the carousel viewer is showing _showSidebar?: boolean; //whether an annotationsidebar should be displayed for text docuemnts _singleLine?: boolean; // whether text document is restricted to a single line (carriage returns make new document) + "_carousel-caption-xMargin"?: number; + "_carousel-caption-yMargin"?: number; x?: number; y?: number; z?: number; diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index bfb7134b2..08d49f8a5 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -64,8 +64,8 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument) borderRadius: StrCast(this.layoutDoc._captionBorderRounding), }}>
; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index fbb27f9b7..3dbe90653 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -86,7 +86,7 @@ export class CurrentUserUtils { if (doc["template-button-detail"] === undefined) { const { TextDocument, MasonryDocument, CarouselDocument } = Docs.Create; - const carousel = CarouselDocument([], { title: "data", _height: 350, _itemIndex: 0, backgroundColor: "#9b9b9b3F" }); + const carousel = CarouselDocument([], { title: "data", _height: 350, _itemIndex: 0, "_carousel-caption-xMargin": 10, "_carousel-caption-yMargin": 10, backgroundColor: "#9b9b9b3F" }); const details = TextDocument("", { title: "details", _height: 350, _autoHeight: true }); const short = TextDocument("", { title: "shortDescription", treeViewOpen: true, treeViewExpandedView: "layout", _height: 100, _autoHeight: true }); -- cgit v1.2.3-70-g09d2 From ae21e365e8ed34060bd94ee04d0d8b3a1f3479f3 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 1 May 2020 16:08:35 -0400 Subject: added doubleClick open on right to detailView template for Buxton --- src/client/documents/Documents.ts | 1 + src/client/views/collections/CollectionCarouselView.tsx | 4 +++- src/server/authentication/models/current_user_utils.ts | 12 ++++++++++-- 3 files changed, 14 insertions(+), 3 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 732f9303b..a8b10bc7b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -123,6 +123,7 @@ export interface DocumentOptions { borderRounding?: string; boxShadow?: string; dontRegisterChildren?: boolean; + "onDoubleClick-rawScript"?: string // onDoubleClick script in raw text form "onClick-rawScript"?: string; // onClick script in raw text form "onCheckedClick-rawScript"?: string; // onChecked script in raw text form "onCheckedClick-params"?: List; // parameter list for onChecked treeview functions diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 08d49f8a5..4086294ad 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -4,7 +4,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { documentSchema } from '../../../new_fields/documentSchemas'; import { makeInterface } from '../../../new_fields/Schema'; -import { NumCast, StrCast } from '../../../new_fields/Types'; +import { NumCast, StrCast, ScriptCast } from '../../../new_fields/Types'; import { DragManager } from '../../util/DragManager'; import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; import "./CollectionCarouselView.scss"; @@ -48,6 +48,8 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument) <>
(["heading", "checked", "containingTreeView"]), isTemplateDoc: true, isTemplateForField: "onCheckedClick", _width: 300, _height: 200 }, "onCheckedClick"); - doc.clickFuncs = Docs.Create.TreeDocument([onClick, onCheckedClick], { title: "onClick funcs" }); + doc.clickFuncs = Docs.Create.TreeDocument([onClick, onDoubleClick, onCheckedClick], { title: "onClick funcs" }); } PromiseValue(Cast(doc.clickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast)); -- cgit v1.2.3-70-g09d2 From 5e6352c78be5b2a9fe791bd87da9b2415ced4839 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 1 May 2020 19:35:37 -0400 Subject: added childLayoutTemplate to render collection children w/o modifying them. fixed docComponent's layoutDoc to use LayoutDoc prop. change buxton template to use new template mechanisms. fixed fixed lint errors. --- src/client/documents/Documents.ts | 5 +- src/client/util/DragManager.ts | 2 +- src/client/views/DocComponent.tsx | 2 +- src/client/views/animationtimeline/Timeline.tsx | 4 +- .../views/animationtimeline/TimelineMenu.tsx | 81 +++++++++++----------- src/client/views/animationtimeline/Track.tsx | 50 ++++++------- .../views/collections/CollectionStackingView.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 17 ----- .../views/collections/CollectionTreeView.tsx | 36 ++++------ .../collectionFreeForm/CollectionFreeFormView.tsx | 11 ++- src/client/views/nodes/PresBox.tsx | 2 +- .../views/nodes/formattedText/DashFieldView.tsx | 2 +- src/new_fields/documentSchemas.ts | 2 +- 13 files changed, 94 insertions(+), 122 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a8b10bc7b..434b26312 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -93,7 +93,8 @@ export interface DocumentOptions { scale?: number; isDisplayPanel?: boolean; // whether the panel functions as GoldenLayout "stack" used to display documents forceActive?: boolean; - layout?: string | Doc; + layout?: string | Doc; // default layout string for a document + childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox or Buxton layout in tree view) hideFilterView?: boolean; // whether to hide the filter popout on collections hideHeadings?: boolean; // whether stacking view column headings should be hidden isTemplateForField?: string; // the field key for which the containing document is a rendering template @@ -123,7 +124,7 @@ export interface DocumentOptions { borderRounding?: string; boxShadow?: string; dontRegisterChildren?: boolean; - "onDoubleClick-rawScript"?: string // onDoubleClick script in raw text form + "onDoubleClick-rawScript"?: string; // onDoubleClick script in raw text form "onClick-rawScript"?: string; // onClick script in raw text form "onCheckedClick-rawScript"?: string; // onChecked script in raw text form "onCheckedClick-params"?: List; // parameter list for onChecked treeview functions diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c48611eff..c06ad3d60 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -316,7 +316,7 @@ export namespace DragManager { return closestDists[minIndex] < snapThreshold ? closestPts[minIndex] + offs[minIndex] : drag; } return drag; - } + }; return { thisX: snapVal([xFromLeft, xFromRight], e.pageX, vertSnapLines), thisY: snapVal([yFromTop, yFromBottom], e.pageY, horizSnapLines) }; } diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 0a8f0c9a7..629b0f447 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -20,7 +20,7 @@ export function DocComponent

(schemaCtor: (doc: D // This is the "The Document" -- it encapsulates, data, layout, and any templates @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; } // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info - @computed get layoutDoc() { return Doc.Layout(this.props.Document); } + @computed get layoutDoc() { return Doc.Layout(this.props.Document, this.props.LayoutDoc?.()); } // This is the data part of a document -- ie, the data that is constant across all views of the document @computed get dataDoc() { return this.props.Document[DataSym] as Doc; } diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 77656b85f..466cbb867 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -525,8 +525,8 @@ export class Timeline extends React.Component { @action.bound changeLengths() { if (this._infoContainer.current) { - this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; //the visible length of the timeline (the length that you current see) - this._visibleStart = this._infoContainer.current!.scrollLeft; //where the div starts + this._visibleLength = this._infoContainer.current.getBoundingClientRect().width; //the visible length of the timeline (the length that you current see) + this._visibleStart = this._infoContainer.current.scrollLeft; //where the div starts } } diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx index 59c25596e..53ca9acad 100644 --- a/src/client/views/animationtimeline/TimelineMenu.tsx +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -1,7 +1,7 @@ import * as React from "react"; -import {observable, action, runInAction} from "mobx"; -import {observer} from "mobx-react"; -import "./TimelineMenu.scss"; +import { observable, action, runInAction } from "mobx"; +import { observer } from "mobx-react"; +import "./TimelineMenu.scss"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faChartLine, faRoad, faClipboard, faPen, faTrash, faTable } from "@fortawesome/free-solid-svg-icons"; import { Utils } from "../../../Utils"; @@ -9,67 +9,66 @@ import { Utils } from "../../../Utils"; @observer export class TimelineMenu extends React.Component { - public static Instance:TimelineMenu; + public static Instance: TimelineMenu; @observable private _opacity = 0; - @observable private _x = 0; - @observable private _y = 0; - @observable private _currentMenu:JSX.Element[] = []; + @observable private _x = 0; + @observable private _y = 0; + @observable private _currentMenu: JSX.Element[] = []; - constructor (props:Readonly<{}>){ - super(props); - TimelineMenu.Instance = this; + constructor(props: Readonly<{}>) { + super(props); + TimelineMenu.Instance = this; } - + @action - openMenu = (x?:number, y?:number) => { - this._opacity = 1; - x ? this._x = x : this._x = 0; - y ? this._y = y : this._y = 0; + openMenu = (x?: number, y?: number) => { + this._opacity = 1; + x ? this._x = x : this._x = 0; + y ? this._y = y : this._y = 0; } @action closeMenu = () => { - this._opacity = 0; - this._currentMenu = []; - this._x = -1000000; - this._y = -1000000; + this._opacity = 0; + this._currentMenu = []; + this._x = -1000000; + this._y = -1000000; } @action - addItem = (type: "input" | "button", title: string, event: (e:any, ...args:any[]) => void) => { - if (type === "input"){ - let inputRef = React.createRef(); - let text = ""; - this._currentMenu.push(

{ + addItem = (type: "input" | "button", title: string, event: (e: any, ...args: any[]) => void) => { + if (type === "input") { + const inputRef = React.createRef(); + let text = ""; + this._currentMenu.push(
{ e.stopPropagation(); text = e.target.value; }} onKeyDown={(e) => { if (e.keyCode === 13) { - event(text); - this.closeMenu(); - e.stopPropagation(); - } - }}/>
); + event(text); + this.closeMenu(); + e.stopPropagation(); + } + }} />
); } else if (type === "button") { - let buttonRef = React.createRef(); - this._currentMenu.push(

{ - e.preventDefault(); - e.stopPropagation(); - event(e); - this.closeMenu(); - }}>{title}

); - } + this._currentMenu.push(

{ + e.preventDefault(); + e.stopPropagation(); + event(e); + this.closeMenu(); + }}>{title}

); + } } - @action - addMenu = (title:string) => { - this._currentMenu.unshift(

{title}

); + @action + addMenu = (title: string) => { + this._currentMenu.unshift(

{title}

); } render() { return ( -
+
{this._currentMenu}
); diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 79eb60fae..461db4858 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -90,9 +90,9 @@ export class Track extends React.Component { */ @action saveKeyframe = async () => { - let keyframes = Cast(this.saveStateRegion?.keyframes, listSpec(Doc)) as List; - let kfIndex = keyframes.indexOf(this.saveStateKf!); - let kf = keyframes[kfIndex] as Doc; //index in the keyframe + const keyframes = Cast(this.saveStateRegion?.keyframes, listSpec(Doc)) as List; + const kfIndex = keyframes.indexOf(this.saveStateKf!); + const kf = keyframes[kfIndex] as Doc; //index in the keyframe if (this._newKeyframe) { DocListCast(this.saveStateRegion?.keyframes).forEach((kf, index) => { this.copyDocDataToKeyFrame(kf); @@ -103,17 +103,17 @@ export class Track extends React.Component { if (!kf) return; if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades this.copyDocDataToKeyFrame(kf); - let leftkf = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists - let rightkf = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists + const leftkf = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists + const rightkf = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists if (leftkf?.type === KeyframeFunc.KeyframeType.fade) { //replicating this keyframe to fades - let edge = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf); + const edge = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf); edge && this.copyDocDataToKeyFrame(edge); leftkf && this.copyDocDataToKeyFrame(leftkf); - edge && (edge!.opacity = 0.1); - leftkf && (leftkf!.opacity = 1); + edge && (edge.opacity = 0.1); + leftkf && (leftkf.opacity = 1); } if (rightkf?.type === KeyframeFunc.KeyframeType.fade) { - let edge = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, rightkf); + const edge = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, rightkf); edge && this.copyDocDataToKeyFrame(edge); rightkf && this.copyDocDataToKeyFrame(rightkf); edge && (edge.opacity = 0.1); @@ -142,7 +142,7 @@ export class Track extends React.Component { //check for region const region = this.findRegion(this.time); if (region !== undefined) { //if region at scrub time exist - let r = region as RegionData; //for some region is returning undefined... which is not the case + const r = region as RegionData; //for some region is returning undefined... which is not the case if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined) { //basically when there is no additional keyframe at that timespot this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default); } @@ -222,11 +222,11 @@ export class Track extends React.Component { } else if (this._newKeyframe) { await this.saveKeyframe(); } - let regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on + const regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on if (regiondata) { - let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists - let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists - let currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe + const leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists + const rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists + const currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe if (currentkf) { console.log("is current"); await this.applyKeys(currentkf); @@ -248,7 +248,7 @@ export class Track extends React.Component { if (!kf[key]) { this.props.node[key] = undefined; } else { - let stored = kf[key]; + const stored = kf[key]; this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored; } }); @@ -261,7 +261,7 @@ export class Track extends React.Component { @action calcCurrent = (region: Doc) => { let currentkf: (Doc | undefined) = undefined; - let keyframes = DocListCast(region.keyframes!); + const keyframes = DocListCast(region.keyframes!); keyframes.forEach((kf) => { if (NumCast(kf.time) === Math.round(this.time)) currentkf = kf; }); @@ -276,12 +276,12 @@ export class Track extends React.Component { interpolate = async (left: Doc, right: Doc) => { this.primitiveWhitelist.forEach(key => { if (left[key] && right[key] && typeof (left[key]) === "number" && typeof (right[key]) === "number") { //if it is number, interpolate - let dif = NumCast(right[key]) - NumCast(left[key]); - let deltaLeft = this.time - NumCast(left.time); - let ratio = deltaLeft / (NumCast(right.time) - NumCast(left.time)); + const dif = NumCast(right[key]) - NumCast(left[key]); + const deltaLeft = this.time - NumCast(left.time); + const ratio = deltaLeft / (NumCast(right.time) - NumCast(left.time)); this.props.node[key] = NumCast(left[key]) + (dif * ratio); } else { // case data - let stored = left[key]; + const stored = left[key]; this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored; } }); @@ -301,8 +301,8 @@ export class Track extends React.Component { */ @action onInnerDoubleClick = (e: React.MouseEvent) => { - let inner = this._inner.current!; - let offsetX = Math.round((e.clientX - inner.getBoundingClientRect().left) * this.props.transform.Scale); + const inner = this._inner.current!; + const offsetX = Math.round((e.clientX - inner.getBoundingClientRect().left) * this.props.transform.Scale); this.createRegion(KeyframeFunc.convertPixelTime(offsetX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); } @@ -313,10 +313,10 @@ export class Track extends React.Component { @action createRegion = (time: number) => { if (this.findRegion(time) === undefined) { //check if there is a region where double clicking (prevents phantom regions) - let regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data + const regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data regiondata.position = time; //set position - let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions); + const rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions); if (rightRegion && rightRegion.position - regiondata.position <= 4000) { //edge case when there is less than default 4000 duration space between this and right region regiondata.duration = rightRegion.position - regiondata.position; @@ -332,7 +332,7 @@ export class Track extends React.Component { @action makeKeyData = (regiondata: RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time - const trackKeyFrames = DocListCast(regiondata.keyframes)!; + const trackKeyFrames = DocListCast(regiondata.keyframes); const existingkf = trackKeyFrames.find(TK => TK.time === time); if (existingkf) return existingkf; //else creates a new doc. diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 6c230d5b1..01766f65f 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -128,7 +128,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { return layoutDoc._fitWidth ? wid * NumCast(layoutDoc.scrollHeight, nh) / (nw || 1) : layoutDoc[HeightSym](); } componentDidMount() { - super.componentDidMount(); + super.componentDidMount?.(); // reset section headers when a new filter is inputted this._pivotFieldDisposer = reaction( diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 8cc1af55b..e44bbae78 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -58,7 +58,6 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: private dropDisposer?: DragManager.DragDropDisposer; private gestureDisposer?: GestureUtils.GestureEventDisposer; protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; - private _childLayoutDisposer?: IReactionDisposer; protected _mainCont?: HTMLDivElement; protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this.dropDisposer?.(); @@ -75,25 +74,9 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: this.createDashEventsTarget(ele); } - componentDidMount() { - this._childLayoutDisposer = reaction(() => ({ childDocs: this.childDocs, childLayout: Cast(this.props.Document.childLayout, Doc) }), - ({ childDocs, childLayout }) => { - if (childLayout instanceof Doc) { - childDocs.map(doc => { - doc.layout_fromParent = childLayout; - doc.layoutKey = "layout_fromParent"; - }); - } - else if (!(childLayout instanceof Promise)) { - childDocs.filter(d => !d.isTemplateForField).map(doc => doc.layoutKey === "layout_fromParent" && (doc.layoutKey = "layout")); - } - }, { fireImmediately: true }); - - } componentWillUnmount() { this.gestureDisposer?.(); this.multiTouchDisposer?.(); - this._childLayoutDisposer?.(); } @computed get dataDoc() { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 71358a8ec..296c1a39c 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -725,14 +725,6 @@ export class CollectionTreeView extends CollectionSubView { - DocListCast(this.dataDoc[this.props.fieldKey]).map(d => { - DocListCast(d.data).map((img, i) => { - const caption = (d.captions as any)[i]; - if (caption) { - Doc.GetProto(img).caption = caption; - } - }); - }); const { ImageDocument } = Docs.Create; const { Document } = this.props; const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif"; @@ -742,21 +734,19 @@ export class CollectionTreeView extends CollectionSubView(["dropAction"]), icon: "portrait", - onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), - })); - - Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", - Docs.Create.FontIconDocument({ - title: "detail view", _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias", - dragFactory: detailView, removeDropProperties: new List(["dropAction"]), icon: "file-alt", - onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), - })); - - Document.childLayout = heroView; + const doubleClickView = ImageDocument("http://cs.brown.edu/~bcz/face.gif", { _width: 400 }); // replace with desired double click target + DocListCast(this.dataDoc[this.props.fieldKey]).map(d => { + DocListCast(d.data).map((img, i) => { + const caption = (d.captions as any)[i]; + if (caption) { + Doc.GetProto(img).caption = caption; + Doc.GetProto(img).doubleClickView = doubleClickView; + } + }); + d.layout = ImageBox.LayoutString("hero"); + }); + + Document.childLayoutTemplate = heroView; Document.childDetailView = detailView; Document._viewType = CollectionViewType.Time; Document._forceActive = true; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b4eb22444..45ef0455e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -855,9 +855,10 @@ export class CollectionFreeFormView extends CollectionSubView BoolCast(this.Document.useClusters); @computed get backgroundActive() { return this.layoutDoc.isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); } + backgroundHalo = () => BoolCast(this.Document.useClusters); parentActive = () => this.props.active() || this.backgroundActive ? true : false; + childLayoutFunc = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null); getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps { return { ...this.props, @@ -867,12 +868,12 @@ export class CollectionFreeFormView extends CollectionSubView this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null); get doLayoutComputation() { const { newPool, computedElementData } = this.doInternalLayoutComputation; runInAction(() => @@ -1025,7 +1025,6 @@ export class CollectionFreeFormView extends CollectionSubView this.doLayoutComputation, (elements) => this._layoutElements = elements || [], { fireImmediately: true, name: "doLayout" }); @@ -1156,7 +1155,7 @@ export class CollectionFreeFormView extends CollectionSubView !doc.isBackground && doc.z === undefined).map(doc => isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 72bbc9e4b..6e3420f22 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -260,7 +260,7 @@ export class PresBox extends ViewBoxBaseComponent panelHeight = () => this.props.PanelHeight() - 20; active = (outsideReaction?: boolean) => ((InkingControl.Instance.selectedTool === InkTool.None && !this.layoutDoc.isBackground) && - (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false); + (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false) whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive)); diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 1b22ed4cd..d87d6e424 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -104,7 +104,7 @@ export class DashFieldViewInternal extends React.Component this._showEnumerables = true)); }} > {strVal} - + ; } } } diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index 7bf1c03c8..fd9a304f9 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -78,7 +78,7 @@ export const positionSchema = createSchema({ }); export const collectionSchema = createSchema({ - childLayout: Doc, // layout template for children of a collecion + childLayoutTemplate: Doc, // layout template for children of a collecion childDetailView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to use this field) onChildClick: ScriptField, // script to run for each child when its clicked onChildDoubleClick: ScriptField, // script to run for each child when its clicked -- cgit v1.2.3-70-g09d2 From 40d5c3acab6dbdc67f6d4bfd15c802da9fe08ca0 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 2 May 2020 00:17:21 -0400 Subject: cleaned up a lot of layoutTemplate/String props. fixed link drawing. --- src/client/documents/Documents.ts | 7 +- src/client/util/DocumentManager.ts | 2 +- src/client/views/DocComponent.tsx | 4 +- src/client/views/DocumentDecorations.scss | 24 +++--- src/client/views/DocumentDecorations.tsx | 4 +- src/client/views/MainView.scss | 6 +- .../views/collections/CollectionCarouselView.tsx | 11 +-- .../views/collections/CollectionStackingView.tsx | 29 ++++--- src/client/views/collections/CollectionSubView.tsx | 2 + .../views/collections/CollectionTimeView.tsx | 4 +- .../views/collections/CollectionTreeView.tsx | 8 +- src/client/views/collections/CollectionView.tsx | 14 +++- .../views/collections/CollectionViewChromes.tsx | 6 +- .../CollectionFreeFormLinkView.tsx | 2 +- .../CollectionFreeFormLinksView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 10 +-- .../CollectionMulticolumnView.tsx | 5 +- .../CollectionMultirowView.tsx | 5 +- src/client/views/nodes/AudioBox.tsx | 4 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 4 +- .../views/nodes/ContentFittingDocumentView.tsx | 5 +- src/client/views/nodes/DocumentBox.tsx | 94 ++++++++++++++------- src/client/views/nodes/DocumentContentsView.tsx | 44 +++++----- src/client/views/nodes/DocumentView.tsx | 29 +++---- src/client/views/nodes/FieldView.tsx | 3 +- src/client/views/nodes/KeyValueBox.tsx | 2 +- src/client/views/nodes/LinkAnchorBox.tsx | 1 - src/client/views/nodes/ScreenshotBox.tsx | 6 +- src/client/views/nodes/VideoBox.tsx | 6 +- src/new_fields/Doc.ts | 2 +- src/new_fields/documentSchemas.ts | 95 ++++++++++++---------- .../authentication/models/current_user_utils.ts | 2 +- 32 files changed, 247 insertions(+), 195 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 434b26312..672f94f75 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -95,6 +95,7 @@ export interface DocumentOptions { forceActive?: boolean; layout?: string | Doc; // default layout string for a document childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox or Buxton layout in tree view) + childLayoutString?: string; // template string for collection to use to render its children hideFilterView?: boolean; // whether to hide the filter popout on collections hideHeadings?: boolean; // whether stacking view column headings should be hidden isTemplateForField?: string; // the field key for which the containing document is a rendering template @@ -123,7 +124,7 @@ export interface DocumentOptions { displayTimecode?: number; // the time that a document should be displayed (e.g., time an annotation should be displayed on a video) borderRounding?: string; boxShadow?: string; - dontRegisterChildren?: boolean; + dontRegisterChildViews?: boolean; "onDoubleClick-rawScript"?: string; // onDoubleClick script in raw text form "onClick-rawScript"?: string; // onClick script in raw text form "onCheckedClick-rawScript"?: string; // onChecked script in raw text form @@ -592,9 +593,7 @@ export namespace Docs { linkDocProto.anchor1_timecode = source.doc.currentTimecode || source.doc.displayTimecode; linkDocProto.anchor2_timecode = target.doc.currentTimecode || target.doc.displayTimecode; - if (linkDocProto.layout_key1 === undefined) { - Cast(linkDocProto.proto, Doc, null).layout_key1 = LinkAnchorBox.LayoutString("anchor1"); - Cast(linkDocProto.proto, Doc, null).layout_key2 = LinkAnchorBox.LayoutString("anchor2"); + if (linkDocProto.linkBoxExcludedKeys === undefined) { Cast(linkDocProto.proto, Doc, null).linkBoxExcludedKeys = new List(["treeViewExpandedView", "treeViewHideTitle", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "aliasNumber", "isPrototype", "lastOpened", "creationDate", "author"]); Cast(linkDocProto.proto, Doc, null).layoutKey = undefined; } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 4683e77a8..1ba6f0248 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -117,7 +117,7 @@ export class DocumentManager { pairs.push(...linksList.reduce((pairs, link) => { const linkToDoc = link && LinkManager.Instance.getOppositeAnchor(link, dv.props.Document); linkToDoc && DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => { - if (dv.props.Document.type !== DocumentType.LINK || dv.props.layoutKey !== docView1.props.layoutKey) { + if (dv.props.Document.type !== DocumentType.LINK || dv.props.LayoutTemplateString !== docView1.props.LayoutTemplateString) { pairs.push({ a: dv, b: docView1, l: link }); } }); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 629b0f447..fd0d2bdbb 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -11,7 +11,7 @@ import { InteractionUtils } from '../util/InteractionUtils'; /// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView) interface DocComponentProps { Document: Doc; - LayoutDoc?: () => Opt; + LayoutTemplate?: () => Opt; } export function DocComponent

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

{ @@ -20,7 +20,7 @@ export function DocComponent

(schemaCtor: (doc: D // This is the "The Document" -- it encapsulates, data, layout, and any templates @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; } // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info - @computed get layoutDoc() { return Doc.Layout(this.props.Document, this.props.LayoutDoc?.()); } + @computed get layoutDoc() { return Doc.Layout(this.props.Document, this.props.LayoutTemplate?.()); } // This is the data part of a document -- ie, the data that is constant across all views of the document @computed get dataDoc() { return this.props.Document[DataSym] as Doc; } diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 61d517d43..4f34eb9e3 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -44,7 +44,6 @@ $linkGap : 3px; .documentDecorations-radius { pointer-events: auto; - background: black; opacity: 1; transform: translate(10px, 10px); grid-row: 4; @@ -88,12 +87,12 @@ $linkGap : 3px; opacity: 1; } #documentDecorations-topLeftResizer { - border-left: black 2px solid; - border-top: black solid 2px; + border-left: 2px solid; + border-top: solid 2px; } #documentDecorations-bottomRightResizer { - border-right: black 2px solid; - border-bottom: black solid 2px; + border-right: 2px solid; + border-bottom: solid 2px; } #documentDecorations-topLeftResizer:hover, #documentDecorations-bottomRightResizer:hover { @@ -111,12 +110,12 @@ $linkGap : 3px; opacity: 1; } #documentDecorations-topRightResizer { - border-right: black 2px solid; - border-top: black 2px solid; + border-right: 2px solid; + border-top: 2px solid; } #documentDecorations-bottomLeftResizer { - border-left: black 2px solid; - border-bottom: black 2px solid; + border-left: 2px solid; + border-bottom: 2px solid; } #documentDecorations-topRightResizer:hover, #documentDecorations-bottomLeftResizer:hover { @@ -136,7 +135,6 @@ $linkGap : 3px; } .documentDecorations-contextMenu { - background: $alt-accent; width: 25px; height: calc(100% + 8px); // 8px for the height of the top resizer bar grid-column-start: 1; @@ -144,14 +142,14 @@ $linkGap : 3px; pointer-events: all; } .documentDecorations-title { - background: $alt-accent; opacity: 1; grid-column-start: 3; grid-column-end: 4; pointer-events: auto; overflow: hidden; text-align: center; - display:flex; + display: flex; + border-bottom: solid 1px; } .publishBox { width: 20px; @@ -168,7 +166,6 @@ $linkGap : 3px; .documentDecorations-closeButton { - background: $alt-accent; opacity: 1; grid-column-start: 4; grid-column-end: 6; @@ -178,7 +175,6 @@ $linkGap : 3px; } .documentDecorations-minimizeButton { - background: $alt-accent; opacity: 1; grid-column-start: 1; grid-column-end: 3; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index e2759291a..396fe12b5 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -4,7 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DataSym, Field } from "../../new_fields/Doc"; -import { PositionDocument } from '../../new_fields/documentSchemas'; +import { Document } from '../../new_fields/documentSchemas'; import { ScriptField } from '../../new_fields/ScriptField'; import { Cast, StrCast, NumCast } from "../../new_fields/Types"; import { Utils, setupMoveUpEvents, emptyFunction, returnFalse, simulateMouseClick } from "../../Utils"; @@ -301,7 +301,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => { if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { - const doc = PositionDocument(element.rootDoc); + const doc = Document(element.rootDoc); let nwidth = doc._nativeWidth || 0; let nheight = doc._nativeHeight || 0; const width = (doc._width || 0); diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 81d427f64..04288a9e1 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -39,7 +39,12 @@ } } +.mainView-container { + color:dimgray; +} + .mainView-container-dark { + color: lightgray; .lm_goldenlayout { background: dimgray; } @@ -54,7 +59,6 @@ } .contextMenu-cont, .contextMenu-item { background: dimGray; - color: lightgray; } .contextMenu-item:hover { background: gray; diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 4086294ad..769b323ae 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -2,9 +2,9 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { observable, computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { documentSchema } from '../../../new_fields/documentSchemas'; +import { documentSchema, collectionSchema } from '../../../new_fields/documentSchemas'; import { makeInterface } from '../../../new_fields/Schema'; -import { NumCast, StrCast, ScriptCast } from '../../../new_fields/Types'; +import { NumCast, StrCast, ScriptCast, Cast } from '../../../new_fields/Types'; import { DragManager } from '../../util/DragManager'; import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; import "./CollectionCarouselView.scss"; @@ -16,8 +16,8 @@ import { ContextMenu } from '../ContextMenu'; import { ObjectField } from '../../../new_fields/ObjectField'; import { returnFalse } from '../../../Utils'; -type CarouselDocument = makeInterface<[typeof documentSchema,]>; -const CarouselDocument = makeInterface(documentSchema); +type CarouselDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>; +const CarouselDocument = makeInterface(documentSchema, collectionSchema); @observer export class CollectionCarouselView extends CollectionSubView(CarouselDocument) { @@ -40,7 +40,6 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument) e.stopPropagation(); this.layoutDoc._itemIndex = (NumCast(this.layoutDoc._itemIndex) - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length; } - panelHeight = () => this.props.PanelHeight() - 50; @computed get content() { const index = NumCast(this.layoutDoc._itemIndex); @@ -51,6 +50,8 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument) onDoubleClick={ScriptCast(this.layoutDoc.onChildDoubleClick)} onClick={ScriptCast(this.layoutDoc.onChildClick)} renderDepth={this.props.renderDepth + 1} + LayoutTemplate={this.props.ChildLayoutTemplate} + LayoutTemplateString={this.props.ChildLayoutString} Document={this.childLayoutPairs[index].layout} DataDoc={this.childLayoutPairs[index].data} PanelHeight={this.panelHeight} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 01766f65f..eb70cec9d 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -4,15 +4,17 @@ import { CursorProperty } from "csstype"; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import Switch from 'rc-switch'; -import { Doc, HeightSym, WidthSym, DataSym } from "../../../new_fields/Doc"; +import { DataSym, Doc, HeightSym, WidthSym } from "../../../new_fields/Doc"; +import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; -import { listSpec } from "../../../new_fields/Schema"; +import { listSpec, makeInterface } from "../../../new_fields/Schema"; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types"; import { TraceMobx } from "../../../new_fields/util"; -import { Utils, setupMoveUpEvents, emptyFunction, returnZero, returnOne, returnFalse } from "../../../Utils"; +import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from "../../../Utils"; import { DragManager, dropActionType } from "../../util/DragManager"; +import { SelectionManager } from "../../util/SelectionManager"; import { Transform } from "../../util/Transform"; import { undoBatch } from "../../util/UndoManager"; import { ContextMenu } from "../ContextMenu"; @@ -24,11 +26,14 @@ import "./CollectionStackingView.scss"; import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn"; import { CollectionSubView } from "./CollectionSubView"; import { CollectionViewType } from "./CollectionView"; -import { SelectionManager } from "../../util/SelectionManager"; +import { ScriptField } from "../../../new_fields/ScriptField"; const _global = (window /* browser */ || global /* node */) as any; +type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>; +const StackingDocument = makeInterface(collectionSchema, documentSchema); + @observer -export class CollectionStackingView extends CollectionSubView(doc => doc) { +export class CollectionStackingView extends CollectionSubView(StackingDocument) { _masonryGridRef: HTMLDivElement | null = null; _draggerRef = React.createRef(); _pivotFieldDisposer?: IReactionDisposer; @@ -116,7 +121,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { getSimpleDocHeight(d?: Doc) { if (!d) return 0; - const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.()); + const layoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.()); const nw = NumCast(layoutDoc._nativeWidth); const nh = NumCast(layoutDoc._nativeHeight); let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1); @@ -160,14 +165,16 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } return this.props.addDocTab(doc, where); } + getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) { - const layoutDoc = Doc.Layout(doc, this.props.childLayoutTemplate?.()); + const layoutDoc = Doc.Layout(doc, this.props.ChildLayoutTemplate?.()); const height = () => this.getDocHeight(doc); return doc) { PanelHeight={height} NativeHeight={returnZero} NativeWidth={returnZero} - fitToBox={BoolCast(this.props.Document._freezeChildDimensions)} + fitToBox={false} rootSelected={this.rootSelected} dropAction={StrCast(this.props.Document.childDropAction) as dropActionType} onClick={this.onChildClickHandler} @@ -199,13 +206,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { getDocWidth(d?: Doc) { if (!d) return 0; - const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.()); + const layoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.()); const nw = NumCast(layoutDoc._nativeWidth); return Math.min(nw && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns); } getDocHeight(d?: Doc) { if (!d) return 0; - const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.()); + const layoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.()); const nw = NumCast(layoutDoc._nativeWidth); const nh = NumCast(layoutDoc._nativeHeight); let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index e44bbae78..aaea13ded 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -43,6 +43,8 @@ export interface CollectionViewProps extends FieldViewProps { export interface SubCollectionViewProps extends CollectionViewProps { CollectionView: Opt; children?: never | (() => JSX.Element[]) | React.ReactNode; + ChildLayoutTemplate?: () => Doc; + ChildLayoutString?: string; childClickScript?: ScriptField; childDoubleClickScript?: ScriptField; freezeChildDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 045134225..a2d4774c8 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -28,7 +28,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { @observable _childClickedScript: Opt; @observable _viewDefDivClick: Opt; async componentDidMount() { - const detailView = (await DocCastAsync(this.props.Document.childDetailView)) || Doc.findTemplate("detailView", StrCast(this.props.Document.type), ""); + const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || Doc.findTemplate("detailView", StrCast(this.props.Document.type), ""); const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List(['dropAction']); useRightSplit(alias, shiftKey); "; runInAction(() => { this._childClickedScript = ScriptField.MakeScript(childText, { this: Doc.name, shiftKey: "boolean" }, { detailView: detailView! }); @@ -84,7 +84,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { @computed get contents() { return

- +
; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 296c1a39c..dc9348664 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -6,7 +6,6 @@ import { observer } from "mobx-react"; import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; -import { RichTextField } from '../../../new_fields/RichTextField'; import { Document, listSpec } from '../../../new_fields/Schema'; import { ComputedField, ScriptField } from '../../../new_fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types'; @@ -15,7 +14,6 @@ import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager"; -import { makeTemplate } from '../../util/DropConverter'; import { Scripting } from '../../util/Scripting'; import { SelectionManager } from '../../util/SelectionManager'; import { Transform } from '../../util/Transform'; @@ -481,7 +479,7 @@ class TreeView extends React.Component { parentActive={returnTrue} whenActiveChanged={emptyFunction} bringToFront={emptyFunction} - dontRegisterView={BoolCast(this.props.treeViewId.dontRegisterChildren)} + dontRegisterView={BoolCast(this.props.treeViewId.dontRegisterChildViews)} ContainingCollectionView={undefined} ContainingCollectionDoc={this.props.containingCollection} />} @@ -743,11 +741,11 @@ export class CollectionTreeView extends CollectionSubView boolean; + filterAddDocument: (doc: Doc) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example) + childLayoutTemplate?: () => Opt; // specify a layout Doc template to use for children of the collection } export interface CollectionRenderProps { @@ -82,6 +83,8 @@ export interface CollectionRenderProps { active: () => boolean; whenActiveChanged: (isActive: boolean) => void; PanelWidth: () => number; + ChildLayoutTemplate?: () => Doc; + ChildLayoutString?: string; } @observer @@ -245,8 +248,8 @@ export class CollectionView extends Touchable this.props.addDocTab(this.props.Document.childLayout as Doc, "onRight"), icon: "project-diagram" }); } - if (this.props.Document.childDetailView instanceof Doc) { - layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childDetailView as Doc, "onRight"), icon: "project-diagram" }); + if (this.props.Document.childClickedOpenTemplateView instanceof Doc) { + layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childClickedOpenTemplateView as Doc, "onRight"), icon: "project-diagram" }); } layoutItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" }); @@ -474,6 +477,7 @@ export class CollectionView extends Touchable
; } + childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null); render() { TraceMobx(); @@ -483,7 +487,9 @@ export class CollectionView extends Touchable click item view", - script: "this.target.childDetailView = getDocTemplate(this.source?.[0])", - immediate: (source: Doc[]) => this.target.childDetailView = Doc.getDocTemplate(source?.[0]), + params: ["target", "source"], title: "=> click clicked open view", + script: "this.target.childClickedOpenTemplateView = getDocTemplate(this.source?.[0])", + immediate: (source: Doc[]) => this.target.childClickedOpenTemplateView = Doc.getDocTemplate(source?.[0]), initialize: emptyFunction, }; _contentCommand = { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index cf12ef382..d67d1993e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -40,7 +40,7 @@ export class CollectionFreeFormLinkView extends React.Component - c.a.props.layoutKey && c.b.props.layoutKey && c.a.props.Document.type === DocumentType.LINK && + c.a.props.Document.type === DocumentType.LINK && c.a.props.bringToFront !== emptyFunction && c.b.props.bringToFront !== emptyFunction // bcz: this prevents links to be drawn to anchors in CollectionTree views -- this is a hack that should be fixed ).map(c => ); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 45ef0455e..707e103fb 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -5,7 +5,7 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; import { Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../../new_fields/Doc"; -import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas"; +import { documentSchema, collectionSchema } from "../../../../new_fields/documentSchemas"; import { Id } from "../../../../new_fields/FieldSymbols"; import { InkData, InkField, InkTool, PointData } from "../../../../new_fields/InkField"; import { List } from "../../../../new_fields/List"; @@ -66,8 +66,8 @@ export const panZoomSchema = createSchema({ fitH: "number" }); -type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchema, typeof positionSchema, typeof pageSchema]>; -const PanZoomDocument = makeInterface(panZoomSchema, documentSchema, positionSchema, pageSchema); +type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof collectionSchema, typeof documentSchema, typeof pageSchema]>; +const PanZoomDocument = makeInterface(panZoomSchema, collectionSchema, documentSchema, pageSchema); export type collectionFreeformViewProps = { forceScaling?: boolean; // whether to force scaling of content (needed by ImageBox) viewDefDivClick?: ScriptField; @@ -858,7 +858,6 @@ export class CollectionFreeFormView extends CollectionSubView BoolCast(this.Document.useClusters); parentActive = () => this.props.active() || this.backgroundActive ? true : false; - childLayoutFunc = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null); getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps { return { ...this.props, @@ -868,7 +867,8 @@ export class CollectionFreeFormView extends CollectionSubView(PositionDocument) { +export class CollectionFreeFormDocumentView extends DocComponent(Document) { @observable _animPos: number[] | undefined = undefined; random(min: number, max: number) { // min should not be equal to max const mseed = Math.abs(this.X * this.Y); diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx index 637fd5acc..3c2c6c87e 100644 --- a/src/client/views/nodes/ContentFittingDocumentView.tsx +++ b/src/client/views/nodes/ContentFittingDocumentView.tsx @@ -14,7 +14,7 @@ import "./ContentFittingDocumentView.scss"; @observer export class ContentFittingDocumentView extends React.Component{ public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive - private get layoutDoc() { return this.props.LayoutDoc?.() || Doc.Layout(this.props.Document); } + private get layoutDoc() { return this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document); } @computed get freezeDimensions() { return this.props.FreezeDimensions; } nativeWidth = () => NumCast(this.layoutDoc?._nativeWidth, this.props.NativeWidth?.() || (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[WidthSym]() : this.props.PanelWidth())); nativeHeight = () => NumCast(this.layoutDoc?._nativeHeight, this.props.NativeHeight?.() || (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[HeightSym]() : this.props.PanelHeight())); @@ -56,7 +56,8 @@ export class ContentFittingDocumentView extends React.Component; -const DocHolderBoxDocument = makeInterface(documentSchema); +type DocHolderBoxSchema = makeInterface<[typeof documentSchema, typeof collectionSchema]>; +const DocHolderBoxDocument = makeInterface(documentSchema, collectionSchema); @observer export class DocHolderBox extends ViewBoxAnnotatableComponent(DocHolderBoxDocument) { @@ -43,7 +45,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent this.toggleLockSelection, icon: "expand-arrows-alt" }); funcs.push({ description: (this.layoutDoc.excludeCollections ? "Include" : "Exclude") + " Collections", event: () => this.layoutDoc.excludeCollections = !this.layoutDoc.excludeCollections, icon: "expand-arrows-alt" }); funcs.push({ description: `${this.layoutDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.layoutDoc.forceActive = !this.layoutDoc.forceActive, icon: "project-diagram" }); - funcs.push({ description: `Show ${this.layoutDoc.childTemplateName !== "keyValue" ? "key values" : "contents"}`, event: () => this.layoutDoc.childTemplateName = this.layoutDoc.childTemplateName ? undefined : "keyValue", icon: "project-diagram" }); + funcs.push({ description: `Show ${this.layoutDoc.childLayoutTemplateName !== "keyValue" ? "key values" : "contents"}`, event: () => this.layoutDoc.childLayoutString = this.layoutDoc.childLayoutString ? undefined : "", icon: "project-diagram" }); ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); } @@ -103,6 +105,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent this.props.PanelWidth() - 2 * this.xPad; pheight = () => this.props.PanelHeight() - 2 * this.yPad; getTransform = () => this.props.ScreenToLocalTransform().translate(-this.xPad, -this.yPad); + isActive = () => this.active() || !this.props.renderDepth; get renderContents() { const containedDoc = Cast(this.dataDoc[this.props.fieldKey], Doc, null); const childTemplateName = StrCast(this.layoutDoc.childTemplateName); @@ -112,33 +115,62 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent; + const contents = !(containedDoc instanceof Doc) ? (null) : this.layoutDoc.childLayoutString ? + : + ; return contents; } render() { diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 4d20d3e2c..749fb98be 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -96,8 +96,6 @@ export class DocumentContentsView extends React.Component boolean, select: (ctrl: boolean) => void, layoutKey: string, - forceLayout?: string, - forceFieldKey?: string, hideOnLeave?: boolean, makeLink?: () => Opt, // function to call when a link is made }> { @@ -105,6 +103,7 @@ export class DocumentContentsView extends React.Componentawaiting layout

"; // const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, this.layoutDoc === this.props.Document ? this.props.layoutKey : "layout")], "string"); // bcz: replaced this with below... is it right? + if (this.props.LayoutTemplateString) return this.props.LayoutTemplateString; const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, "layout")], "string"); if (this.props.layoutKey === "layout_keyValue") { return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString("data")); @@ -127,8 +126,8 @@ export class DocumentContentsView extends React.Component 1 ? splits[0] + splits[1].replace(/{([^{}]|(?R))*}/, replacer4) : ""; // might have been more elegant if javascript supported recursive patterns return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc) ? (null) : - this.props.forceLayout === "FormattedTextBox" && this.props.forceFieldKey ? - - : - { console.log(test); }} - />; + { 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 f1efa48f4..7c7c03db2 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -5,22 +5,21 @@ import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as rp from "request-promise"; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc"; -import { Document, PositionDocument } from '../../../new_fields/documentSchemas'; +import { Document } from '../../../new_fields/documentSchemas'; import { Id } from '../../../new_fields/FieldSymbols'; import { InkTool } from '../../../new_fields/InkField'; -import { RichTextField } from '../../../new_fields/RichTextField'; import { listSpec } from "../../../new_fields/Schema"; import { SchemaHeaderField } from '../../../new_fields/SchemaHeaderField'; import { ScriptField } from '../../../new_fields/ScriptField'; import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { AudioField, ImageField, PdfField, VideoField } from '../../../new_fields/URLField'; +import { ImageField } from '../../../new_fields/URLField'; import { TraceMobx } from '../../../new_fields/util'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils } from "../../../Utils"; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { ClientRecommender } from '../../ClientRecommender'; import { DocServer } from "../../DocServer"; -import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents"; +import { Docs, DocUtils } from "../../documents/Documents"; import { DocumentType } from '../../documents/DocumentTypes'; import { ClientUtils } from '../../util/ClientUtils'; import { DocumentManager } from "../../util/DocumentManager"; @@ -42,6 +41,7 @@ import { InkingControl } from '../InkingControl'; import { KeyphraseQueryView } from '../KeyphraseQueryView'; import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; +import { LinkAnchorBox } from './LinkAnchorBox'; import { RadialMenu } from './RadialMenu'; import React = require("react"); @@ -58,7 +58,8 @@ export interface DocumentViewProps { NativeHeight: () => number; Document: Doc; DataDoc?: Doc; - LayoutDoc?: () => Opt; + LayoutTemplateString?: string; + LayoutTemplate?: () => Opt; LibraryPath: Doc[]; fitToBox?: boolean; contextMenuItems?: () => { script: ScriptField, label: string }[]; @@ -454,8 +455,8 @@ export class DocumentView extends DocComponent(Docu const dY = -1 * Math.sign(dH); if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { - const doc = PositionDocument(this.props.Document); - const layoutDoc = PositionDocument(Doc.Layout(this.props.Document)); + const doc = Document(this.props.Document); + const layoutDoc = Document(Doc.Layout(this.props.Document)); let nwidth = layoutDoc._nativeWidth || 0; let nheight = layoutDoc._nativeHeight || 0; const width = (layoutDoc._width || 0); @@ -984,13 +985,15 @@ export class DocumentView extends DocComponent(Docu @computed get contents() { TraceMobx(); return (<> - (Docu ); } - linkEndpoint = (linkDoc: Doc) => Doc.LinkEndpoint(linkDoc, this.props.Document); // used to decide whether a link anchor view should be created or not. // if it's a tempoarl link (currently just for Audio), then the audioBox will display the anchor and we don't want to display it here. @@ -1049,12 +1051,12 @@ export class DocumentView extends DocComponent(Docu ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection Need a better prop for passing the containing document to the LinkAnchorBox PanelWidth={this.anchorPanelWidth} PanelHeight={this.anchorPanelHeight} - layoutKey={this.linkEndpoint(d)} ContentScaling={returnOne} backgroundColor={returnTransparent} removeDocument={this.hideLinkAnchor} pointerEvents={false} - LayoutDoc={undefined} + LayoutTemplate={undefined} + LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(d, this.props.Document)}`)} />); } @computed get innards() { @@ -1073,8 +1075,7 @@ export class DocumentView extends DocComponent(Docu
`} ContentScaling={this.childScaling} ChromeHeight={this.chromeHeight} isSelected={this.isSelected} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 1efee4f5a..40d55ce38 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -50,13 +50,12 @@ export interface FieldViewProps { setVideoBox?: (player: VideoBox) => void; ContentScaling: () => number; ChromeHeight?: () => number; - childLayoutTemplate?: () => Opt; + RenderData?: () => Doc; // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React) height?: number; width?: number; background?: string; color?: string; - RenderData?: () => Doc; } @observer diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 2970674a2..d43936949 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -36,7 +36,7 @@ export class KeyValueBox extends React.Component { @observable private _keyInput: string = ""; @observable private _valueInput: string = ""; @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); } - get fieldDocToLayout() { return this.props.fieldKey ? Cast(this.props.Document[this.props.fieldKey], Doc, null) : this.props.Document; } + get fieldDocToLayout() { return this.props.Document; } @action onEnterKey = (e: React.KeyboardEvent): void => { diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 6c50abf21..bc36e056e 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -17,7 +17,6 @@ import { LinkEditor } from "../linking/LinkEditor"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { SelectionManager } from "../../util/SelectionManager"; import { TraceMobx } from "../../../new_fields/util"; -import { DocumentView } from "./DocumentView"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 125690dc7..a0ecc9ff5 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -5,7 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, IReactionDisposer, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as rp from 'request-promise'; -import { documentSchema, positionSchema } from "../../../new_fields/documentSchemas"; +import { documentSchema } from "../../../new_fields/documentSchemas"; import { makeInterface } from "../../../new_fields/Schema"; import { Cast, NumCast } from "../../../new_fields/Types"; import { VideoField } from "../../../new_fields/URLField"; @@ -20,8 +20,8 @@ import { FieldView, FieldViewProps } from './FieldView'; import "./ScreenshotBox.scss"; const path = require('path'); -type ScreenshotDocument = makeInterface<[typeof documentSchema, typeof positionSchema]>; -const ScreenshotDocument = makeInterface(documentSchema, positionSchema); +type ScreenshotDocument = makeInterface<[typeof documentSchema]>; +const ScreenshotDocument = makeInterface(documentSchema); library.add(faVideo); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 613929bca..266b7f43f 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -21,14 +21,14 @@ import { DocumentDecorations } from "../DocumentDecorations"; import { InkingControl } from "../InkingControl"; import { FieldView, FieldViewProps } from './FieldView'; import "./VideoBox.scss"; -import { documentSchema, positionSchema } from "../../../new_fields/documentSchemas"; +import { documentSchema } from "../../../new_fields/documentSchemas"; const path = require('path'); export const timeSchema = createSchema({ currentTimecode: "number", // the current time of a video or other linear, time-based document. Note, should really get set on an extension field, but that's more complicated when it needs to be set since the extension doc needs to be found first }); -type VideoDocument = makeInterface<[typeof documentSchema, typeof positionSchema, typeof timeSchema]>; -const VideoDocument = makeInterface(documentSchema, positionSchema, timeSchema); +type VideoDocument = makeInterface<[typeof documentSchema, typeof timeSchema]>; +const VideoDocument = makeInterface(documentSchema, timeSchema); library.add(faVideo); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 71325d94f..9256f82c2 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -772,7 +772,7 @@ export namespace Doc { export function LinkOtherAnchor(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? Cast(linkDoc.anchor2, Doc) as Doc : Cast(linkDoc.anchor1, Doc) as Doc; } - export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "layout_key1" : "layout_key2"; } + export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "1" : "2"; } export function linkFollowUnhighlight() { Doc.UnhighlightAll(); diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index fd9a304f9..e71fc27f3 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -4,13 +4,25 @@ import { Doc } from "./Doc"; import { DateField } from "./DateField"; export const documentSchema = createSchema({ + // content properties type: "string", // enumerated type of document -- should be template-specific (ie, start with an '_') - layout: "string", // this is the native layout string for the document. templates can be added using other fields and setting layoutKey below - layoutKey: "string", // holds the field key for the field that actually holds the current lyoat title: "string", // document title (can be on either data document or layout) - dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias", "copy", "move") - targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") - childDropAction: "string", // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy") + isTemplateForField: "string",// if specified, it indicates the document is a template that renders the specified field + creationDate: DateField, // when the document was created + links: listSpec(Doc), // computed (readonly) list of links associated with this document + + // "Location" properties in a very general sense + currentTimecode: "number", // current play back time of a temporal document (video / audio) + displayTimecode: "number", // the time that a document should be displayed (e.g., time an annotation should be displayed on a video) + inOverlay: "boolean", // whether the document is rendered in an OverlayView which handles selection/dragging differently + x: "number", // x coordinate when in a freeform view + y: "number", // y coordinate when in a freeform view + z: "number", // z "coordinate" - non-zero specifies the overlay layer of a freeformview + zIndex: "number", // zIndex of a document in a freeform view + scrollY: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that ) + scrollTop: "number", // scroll position of a scrollable document (pdf, text, web) + + // appearance properties on the layout _autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents _nativeWidth: "number", // native width of document which determines how much document contents are scaled when the document's width is set _nativeHeight: "number", // " @@ -27,59 +39,57 @@ export const documentSchema = createSchema({ _showAudio: "boolean", // whether to show the audio record icon on documents _freeformLayoutEngine: "string",// the string ID for the layout engine to use to layout freeform view documents _LODdisable: "boolean", // whether to disbale LOD switching for CollectionFreeFormViews - _pivotField: "string", // specifies which field should be used as the timeline/pivot axis + _pivotField: "string", // specifies which field key should be used as the timeline/pivot axis _replacedChrome: "string", // what the default chrome is replaced with. Currently only supports the value of 'replaced' for PresBox's. _chromeStatus: "string", // determines the state of the collection chrome. values allowed are 'replaced', 'enabled', 'disabled', 'collapsed' - _freezeChildDimensions: "boolean", // freezes child document dimensions (e.g., used by time/pivot view to make sure all children will be scaled to fit their display rectangle) _fontSize: "number", _fontFamily: "string", - isInPlaceContainer: "boolean",// whether the marked object will display addDocTab() calls that target "inPlace" destinations - color: "string", // foreground color of document + + // appearance properties on the data document backgroundColor: "string", // background color of document + borderRounding: "string", // border radius rounding of document + boxShadow: "string", // the amount of shadow around the perimeter of a document + color: "string", // foreground color of document + fitToBox: "boolean", // whether freeform view contents should be zoomed/panned to fill the area of the document view + fontSize: "string", + layout: "string", // this is the native layout string for the document. templates can be added using other fields and setting layoutKey below + layoutKey: "string", // holds the field key for the field that actually holds the current lyoat + letterSpacing: "string", opacity: "number", // opacity of document - creationDate: DateField, // when the document was created - links: listSpec(Doc), // computed (readonly) list of links associated with this document + strokeWidth: "number", + textTransform: "string", + treeViewOpen: "boolean", // flag denoting whether the documents sub-tree (contents) is visible or hidden + treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree + treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document) + + // interaction and linking properties + ignoreClick: "boolean", // whether documents ignores input clicks (but does not ignore manipulation and other events) onClick: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) onPointerUp: 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. ie, to drag out copies of the dragFactory document. - removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped - isTemplateForField: "string",// when specifies a field key, then the containing document is a template that renders the specified field - isBackground: "boolean", // whether document is a background element and ignores input events (can only select with marquee) - treeViewOpen: "boolean", // flag denoting whether the documents sub-tree (contents) is visible or hidden - treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree - treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document) - currentTimecode: "number", // current play back time of a temporal document (video / audio) followLinkLocation: "string",// flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab, ) + isInPlaceContainer: "boolean",// whether the marked object will display addDocTab() calls that target "inPlace" destinations + isLinkButton: "boolean", // whether document functions as a link follow button to follow the first link on the document when clicked + isBackground: "boolean", // whether document is a background element and ignores input events (can only select with marquee) lockedPosition: "boolean", // whether the document can be moved (dragged) lockedTransform: "boolean", // whether the document can be panned/zoomed - inOverlay: "boolean", // whether the document is rendered in an OverlayView which handles selection/dragging differently - borderRounding: "string", // border radius rounding of document - heading: "number", // the logical layout 'heading' of this document (used by rule provider to stylize h1 header elements, from h2, etc) - isLinkButton: "boolean", // whether document functions as a link follow button to follow the first link on the document when clicked - ignoreClick: "boolean", // whether documents ignores input clicks (but does not ignore manipulation and other events) - scrollToLinkID: "string", // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc. - scrollY: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that ) - scrollTop: "number", // scroll position of a scrollable document (pdf, text, web) - strokeWidth: "number", - fontSize: "string", - fitToBox: "boolean", // whether freeform view contents should be zoomed/panned to fill the area of the document view - letterSpacing: "string", - textTransform: "string", - childTemplateName: "string" // the name of a template to use to override the layoutKey when rendering a document in DocHolderBox -}); -export const positionSchema = createSchema({ - zIndex: "number", - x: "number", - y: "number", - z: "number", + // drag drop properties + dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document. + dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias", "copy", "move") + targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") + childDropAction: "string", // specify the override for what should happen when the child of a collection is dragged from it and 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 }); + export const collectionSchema = createSchema({ - childLayoutTemplate: Doc, // layout template for children of a collecion - childDetailView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to use this field) + childLayoutTemplateName: "string", // the name of a template to use to override the layoutKey when rendering a document -- ONLY used in DocHolderBox + childLayoutTemplate: Doc, // layout template to use to render children of a collecion + childLayoutString: "string", //layout string to use to render children of a collection + childClickedOpenTemplateView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to read this value and apply template) + dontRegisterChildViews: "boolean", // whether views made of this document are registered so that they can be found when drawing links scrollToLinkID: "string", // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc. onChildClick: ScriptField, // script to run for each child when its clicked onChildDoubleClick: ScriptField, // script to run for each child when its clicked onCheckedClick: ScriptField, // script to run when a checkbox is clicked next to a child in a tree view @@ -87,6 +97,3 @@ export const collectionSchema = createSchema({ export type Document = makeInterface<[typeof documentSchema]>; export const Document = makeInterface(documentSchema); - -export type PositionDocument = makeInterface<[typeof documentSchema, typeof positionSchema]>; -export const PositionDocument = makeInterface(documentSchema, positionSchema); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 2ae42bf52..b9de93559 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -485,7 +485,7 @@ export class CurrentUserUtils { _width: 50, _height: 25, title: "Library", _fontSize: 10, letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", sourcePanel: new PrefetchProxy(Docs.Create.TreeDocument([workspaces, documents, recentlyClosed, doc], { - title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "move", lockedPosition: true, boxShadow: "0 0", dontRegisterChildren: true + title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "move", lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true })) as any as Doc, targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc, onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel;") -- cgit v1.2.3-70-g09d2 From 06bba6d058794c9750e4a61653e89580b351ea2c Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 3 May 2020 11:41:13 -0400 Subject: added DelegateDocument creator. switched default templates to delegates. --- src/client/documents/Documents.ts | 5 +++++ src/client/util/DropConverter.ts | 2 +- src/client/views/collections/CollectionTreeView.tsx | 2 +- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 11 +++++++---- src/server/authentication/models/current_user_utils.ts | 14 +++++++------- 5 files changed, 21 insertions(+), 13 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 672f94f75..8ea8ded0f 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -746,6 +746,10 @@ export namespace Docs { }; return DockDocument(configs.map(c => c.doc), JSON.stringify(layoutConfig), options, id); } + + export function DelegateDocument(proto: Doc, options: DocumentOptions = {}) { + return InstanceFromProto(proto, undefined, options); + } } export namespace Get { @@ -1052,4 +1056,5 @@ export namespace DocUtils { } Scripting.addGlobal("Docs", Docs); +Scripting.addGlobal(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: "child of " + proto.title }); return d; }); diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 60a6bbb3c..b1993d401 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -68,7 +68,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { }); dbox.dragFactory = layoutDoc; dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined; - dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); + dbox.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory)'); } else if (doc.isButtonBar) { dbox.ignoreClick = true; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 835dfc637..6d6f8d316 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -742,7 +742,7 @@ export class CollectionTreeView extends CollectionSubView m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 1000))); const curText = state.doc.textBetween(0, state.doc.content.size, " \n"); const curTemp = Cast(this.props.Document[this.props.fieldKey + "-textTemplate"], RichTextField); - if (!this._applyingChange) { + const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); + const json = JSON.stringify(state.toJSON()); + if (!this._applyingChange && json.replace(/"selection":.*/, "") !== curProto?.Data.replace(/"selection":.*/, "")) { this._applyingChange = true; this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); - if (!curTemp || curText) { // if no template, or there's text, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended) - this.dataDoc[this.props.fieldKey] = new RichTextField(JSON.stringify(state.toJSON()), curText); + if ((!curTemp && !curProto) || curText) { // if no template, or there's text, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended) + this.dataDoc[this.props.fieldKey] = new RichTextField(json, curText); this.dataDoc[this.props.fieldKey + "-noTemplate"] = (curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited } else { // if we've deleted all the text in a note driven by a template, then restore the template data - this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse(curTemp.Data))); + this.dataDoc[this.props.fieldKey] = undefined; + this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse((curProto || curTemp).Data))); this.dataDoc[this.props.fieldKey + "-noTemplate"] = undefined; // mark the data field as not being split from any template it might have } this._applyingChange = false; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index a5b84a0a1..b63a3dbbf 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -49,7 +49,7 @@ export class CurrentUserUtils { ); queryTemplate.isTemplateDoc = makeTemplate(queryTemplate); doc["template-button-query"] = CurrentUserUtils.ficon({ - onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), + onDragStart: ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)'), dragFactory: new PrefetchProxy(queryTemplate) as any as Doc, removeDropProperties: new List(["dropAction"]), title: "query view", icon: "question-circle" }); @@ -65,7 +65,7 @@ export class CurrentUserUtils { ); slideTemplate.isTemplateDoc = makeTemplate(slideTemplate); doc["template-button-slides"] = CurrentUserUtils.ficon({ - onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), + onDragStart: ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)'), dragFactory: new PrefetchProxy(slideTemplate) as any as Doc, removeDropProperties: new List(["dropAction"]), title: "presentation slide", icon: "address-card" }); @@ -74,12 +74,12 @@ export class CurrentUserUtils { if (doc["template-button-description"] === undefined) { const descriptionTemplate = Docs.Create.TextDocument(" ", { title: "header", _height: 100 }, "header"); // text needs to be a space to allow templateText to be created Doc.GetProto(descriptionTemplate).layout = - "
" + - "
"; + "
" + + "
"; descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate, true, "descriptionView"); doc["template-button-description"] = CurrentUserUtils.ficon({ - onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), + onDragStart: ScriptField.MakeFunction('makeDelegate(this.dragFactory)'), dragFactory: new PrefetchProxy(descriptionTemplate) as any as Doc, removeDropProperties: new List(["dropAction"]), title: "description view", icon: "window-maximize" }); @@ -129,7 +129,7 @@ export class CurrentUserUtils { long.title = "Long Description"; doc["template-button-detail"] = CurrentUserUtils.ficon({ - onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), + onDragStart: ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)'), dragFactory: new PrefetchProxy(detailView) as any as Doc, removeDropProperties: new List(["dropAction"]), title: "detail view", icon: "window-maximize" }); @@ -138,7 +138,7 @@ export class CurrentUserUtils { if (doc["template-buttons"] === undefined) { doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument([doc["template-button-slides"] as Doc, doc["template-button-description"] as Doc, doc["template-button-query"] as Doc, doc["template-button-detail"] as Doc], { - title: "Compound Item Creators", _xMargin: 0, _showTitle: "title", + title: "Document Prototypes", _xMargin: 0, _showTitle: "title", _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), })); -- cgit v1.2.3-70-g09d2 From 3d5bc514936ba804eaa24e9ca1f4619bcaf81eb5 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 3 May 2020 23:59:35 -0400 Subject: extended documentBox's to support drag and drop of style. reorganized imports to avoid some cycles --- src/client/documents/Documents.ts | 10 +- src/client/util/DragManager.ts | 112 +++++++-------------- src/client/util/DropConverter.ts | 2 + src/client/util/SelectionManager.ts | 5 - src/client/views/DocumentDecorations.tsx | 5 +- src/client/views/MainView.tsx | 8 +- .../views/collections/CollectionDockingView.tsx | 5 +- .../collections/CollectionMasonryViewFieldRow.tsx | 2 +- .../views/collections/CollectionPileView.tsx | 5 +- .../views/collections/CollectionSchemaCells.tsx | 2 +- .../CollectionSchemaMovableTableHOC.tsx | 4 +- .../views/collections/CollectionStackingView.tsx | 4 +- .../CollectionStackingViewFieldColumn.tsx | 4 +- .../views/collections/CollectionTreeView.tsx | 4 +- .../CollectionFreeFormLinksView.tsx | 3 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 6 +- src/client/views/linking/LinkMenuGroup.tsx | 4 +- src/client/views/linking/LinkMenuItem.tsx | 39 ++++++- src/client/views/nodes/AudioBox.tsx | 10 +- .../views/nodes/ContentFittingDocumentView.tsx | 8 +- src/client/views/nodes/DocumentBox.scss | 3 +- src/client/views/nodes/DocumentBox.tsx | 57 +++++++---- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/QueryBox.tsx | 3 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 13 ++- .../authentication/models/current_user_utils.ts | 27 +++-- 26 files changed, 189 insertions(+), 158 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 8ea8ded0f..45687f597 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -139,6 +139,7 @@ export interface DocumentOptions { ischecked?: ScriptField; // returns whether a font icon box is checked activePen?: Doc; // which pen document is currently active (used as the radio button state for the 'unhecked' pen tool scripts) onClick?: ScriptField; + onDoubleClick?: ScriptField; onChildClick?: ScriptField; // script given to children of a collection to execute when they are clicked onChildDoubleClick?: ScriptField; // script given to children of a collection to execute when they are double clicked onPointerDown?: ScriptField; @@ -493,7 +494,7 @@ export namespace Docs { const dataDoc = MakeDataDelegate(proto, protoProps, data, fieldKey); const viewDoc = Doc.MakeDelegate(dataDoc, delegId); - viewDoc.type !== DocumentType.LINK && AudioBox.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: viewDoc }, { doc: d }, "audio link", "audio timeline")); + viewDoc.type !== DocumentType.LINK && DocUtils.MakeLinkToActiveAudio(viewDoc); return Doc.assign(viewDoc, delegateProps, true); } @@ -645,7 +646,7 @@ export namespace Docs { } export function DocumentDocument(document?: Doc, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.DOCHOLDER), document, { title: document ? document.title + "" : "container", ...options }); + return InstanceFromProto(Prototypes.get(DocumentType.DOCHOLDER), document, { title: document ? document.title + "" : "container", targetDropAction: "move", ...options }); } export function FreeformDocument(documents: Array, options: DocumentOptions, id?: string) { @@ -1005,6 +1006,11 @@ export namespace DocUtils { }); } + export let ActiveRecordings: Doc[] = []; + + export function MakeLinkToActiveAudio(doc: Doc) { + DocUtils.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: doc }, { doc: d }, "audio link", "audio timeline")); + } export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", id?: string) { const sv = DocumentManager.Instance.getDocumentView(source.doc); if (sv && sv.props.ContainingCollectionDoc === target.doc) return; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 041f2fc2c..d91eb60ef 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,26 +1,16 @@ -import { Doc, Field, DocListCast } from "../../new_fields/Doc"; -import { Cast, ScriptCast, StrCast, NumCast } from "../../new_fields/Types"; -import { emptyFunction } from "../../Utils"; -import { CollectionDockingView } from "../views/collections/CollectionDockingView"; -import * as globalCssVariables from "../views/globalCssVariables.scss"; -import { DocumentManager } from "./DocumentManager"; -import { LinkManager } from "./LinkManager"; -import { SelectionManager } from "./SelectionManager"; -import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField"; -import { Docs, DocUtils } from "../documents/Documents"; -import { ScriptField } from "../../new_fields/ScriptField"; +import { action, observable, runInAction } from "mobx"; +import { DateField } from "../../new_fields/DateField"; +import { Doc, Field, Opt } from "../../new_fields/Doc"; 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"; -import { AudioBox } from "../views/nodes/AudioBox"; -import { DateField } from "../../new_fields/DateField"; -import { DocumentView } from "../views/nodes/DocumentView"; +import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField"; +import { ScriptField } from "../../new_fields/ScriptField"; +import { Cast, NumCast, ScriptCast, StrCast } from "../../new_fields/Types"; +import { emptyFunction } from "../../Utils"; +import { Docs, DocUtils } from "../documents/Documents"; +import * as globalCssVariables from "../views/globalCssVariables.scss"; import { UndoManager } from "./UndoManager"; -import { PointData } from "../../new_fields/InkField"; -import { MainView } from "../views/MainView"; -import { action } from "mobx"; export type dropActionType = "alias" | "copy" | "move" | undefined; // undefined = move export function SetupDrag( @@ -56,10 +46,10 @@ export function SetupDrag( const onItemDown = async (e: React.PointerEvent) => { if (e.button === 0) { e.stopPropagation(); - if (e.shiftKey && CollectionDockingView.Instance) { + if (e.shiftKey) { e.persist(); const dragDoc = await docFunc(); - dragDoc && CollectionDockingView.Instance.StartOtherDrag({ + dragDoc && DragManager.Vals.Instance.StartWindowDrag?.({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, @@ -76,8 +66,19 @@ export function SetupDrag( export namespace DragManager { let dragDiv: HTMLDivElement; - export let horizSnapLines: number[] = []; - export let vertSnapLines: number[] = []; + export class Vals { + static Instance: Vals = new Vals(); + @observable public IsDragging: boolean = false; + @observable public horizSnapLines: number[] = []; + @observable public vertSnapLines: number[] = []; + public StartWindowDrag: Opt<((e: any, dragDocs: Doc[]) => void)> = undefined; + public SetIsDragging(dragging: boolean) { runInAction(() => this.IsDragging = dragging); } + public GetIsDragging() { return this.IsDragging; } + public clearSnapLines() { + this.vertSnapLines.length = 0; + this.horizSnapLines.length = 0; + } + } export function Root() { const root = document.getElementById("root"); @@ -215,7 +216,7 @@ export namespace DragManager { export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { const addAudioTag = (dropDoc: any) => { dropDoc && !dropDoc.creationDate && (dropDoc.creationDate = new DateField); - dropDoc instanceof Doc && AudioBox.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: dropDoc }, { doc: d }, "audio link", "audio timeline")); + dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(dropDoc); return dropDoc; }; const batch = UndoManager.StartBatch("dragging"); @@ -246,40 +247,6 @@ export namespace DragManager { StartDrag(eles, new DragManager.DocumentDragData([]), downX, downY, options, finishDrag); } - // drag links and drop link targets (aliasing them if needed) - export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: DocumentView, downX: number, downY: number, sourceDoc: Doc, specificLinks?: Doc[]) { - const draggedDocs = (specificLinks ? specificLinks : DocListCast(sourceDoc.links)).map(link => LinkManager.Instance.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[]; - - if (draggedDocs.length) { - const moddrag: Doc[] = []; - for (const draggedDoc of draggedDocs) { - const doc = await Cast(draggedDoc.annotationOn, Doc); - if (doc) moddrag.push(doc); - } - - const dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs); - dragData.moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (doc: Doc) => boolean): boolean => { - docView.props.removeDocument?.(doc); - addDocument(doc); - return true; - }; - const containingView = docView.props.ContainingCollectionView; - const finishDrag = (e: DragCompleteEvent) => - e.docDragData && (e.docDragData.droppedDocuments = - dragData.draggedDocuments.reduce((droppedDocs, d) => { - const dvs = DocumentManager.Instance.getDocumentViews(d).filter(dv => dv.props.ContainingCollectionView === containingView); - if (dvs.length) { - dvs.forEach(dv => droppedDocs.push(dv.props.Document)); - } else { - droppedDocs.push(Doc.MakeAlias(d)); - } - return droppedDocs; - }, [] as Doc[])); - - StartDrag([dragEle], dragData, downX, downY, undefined, finishDrag); - } - } - // drag&drop the pdf annotation anchor which will create a text note on drop via a dropCompleted() DragOption export function StartPdfAnnoDrag(eles: HTMLElement[], dragData: PdfAnnoDragData, downX: number, downY: number, options?: DragOptions) { StartDrag(eles, dragData, downX, downY, options); @@ -300,10 +267,8 @@ export namespace DragManager { } export function SetSnapLines(horizLines: number[], vertLines: number[]) { - horizSnapLines = horizLines; - vertSnapLines = vertLines; - MainView.Instance._hLines = horizLines; - MainView.Instance._vLines = vertLines; + DragManager.Vals.Instance.horizSnapLines.push(...horizLines); + DragManager.Vals.Instance.vertSnapLines.push(...vertLines); } export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) { @@ -320,10 +285,13 @@ export namespace DragManager { return drag; }; - return { thisX: snapVal([xFromLeft, xFromRight], e.pageX, vertSnapLines), thisY: snapVal([yFromTop, yFromBottom], e.pageY, horizSnapLines) }; + return { + thisX: snapVal([xFromLeft, xFromRight], e.pageX, DragManager.Vals.Instance.vertSnapLines), + thisY: snapVal([yFromTop, yFromBottom], e.pageY, DragManager.Vals.Instance.horizSnapLines) + }; } export let docsBeingDragged: Doc[] = []; - function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) { + export function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) { eles = eles.filter(e => e); if (!dragDiv) { dragDiv = document.createElement("div"); @@ -331,7 +299,7 @@ export namespace DragManager { dragDiv.style.pointerEvents = "none"; DragManager.Root().appendChild(dragDiv); } - SelectionManager.SetIsDragging(true); + DragManager.Vals.Instance.SetIsDragging(true); const scaleXs: number[] = []; const scaleYs: number[] = []; const xs: number[] = []; @@ -413,14 +381,14 @@ export namespace DragManager { if (dragData instanceof DocumentDragData) { dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : undefined; } - if (e.shiftKey && CollectionDockingView.Instance && dragData.droppedDocuments.length === 1) { + if (e.shiftKey && dragData.droppedDocuments.length === 1) { !dragData.dropAction && (dragData.dropAction = alias); if (dragData.dropAction === "move") { dragData.removeDocument?.(dragData.draggedDocuments[0]); } AbortDrag(); finishDrag?.(new DragCompleteEvent(true, dragData)); - CollectionDockingView.Instance.StartOtherDrag({ + DragManager.Vals.Instance.StartWindowDrag?.({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, @@ -447,22 +415,19 @@ export namespace DragManager { const endDrag = action(() => { document.removeEventListener("pointermove", moveHandler, true); document.removeEventListener("pointerup", upHandler); - MainView.Instance._hLines = []; - MainView.Instance._vLines = []; - vertSnapLines.length = 0; - horizSnapLines.length = 0; + DragManager.Vals.Instance.clearSnapLines(); }); AbortDrag = () => { hideDragShowOriginalElements(); - SelectionManager.SetIsDragging(false); + DragManager.Vals.Instance.SetIsDragging(false); options?.dragComplete?.(new DragCompleteEvent(true, dragData)); endDrag(); }; const upHandler = (e: PointerEvent) => { hideDragShowOriginalElements(); dispatchDrag(eles, e, dragData, xFromLeft, yFromTop, xFromRight, yFromBottom, options, finishDrag); - SelectionManager.SetIsDragging(false); + DragManager.Vals.Instance.SetIsDragging(false); endDrag(); options?.dragComplete?.(new DragCompleteEvent(false, dragData)); }; @@ -520,4 +485,3 @@ 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 index b1993d401..d6db882b8 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -7,6 +7,7 @@ import { Docs } from "../documents/Documents"; import { ScriptField, ComputedField } from "../../new_fields/ScriptField"; import { RichTextField } from "../../new_fields/RichTextField"; import { ImageField } from "../../new_fields/URLField"; +import { Scripting } from "./Scripting"; // // converts 'doc' into a template that can be used to render other documents. @@ -75,3 +76,4 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { data.droppedDocuments[i] = dbox; }); } +Scripting.addGlobal(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); }); \ No newline at end of file diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index a49977c42..d509168b6 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -8,10 +8,8 @@ export namespace SelectionManager { class Manager { - @observable IsDragging: boolean = false; SelectedDocuments: ObservableMap = new ObservableMap(); - @action SelectDoc(docView: DocumentView, ctrlPressed: boolean): void { // if doc is not in SelectedDocuments, add it @@ -78,9 +76,6 @@ export namespace SelectionManager { if (found) manager.SelectDoc(found, false); } - export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); } - export function GetIsDragging() { return manager.IsDragging; } - export function SelectedDocuments(): Array { return Array.from(manager.SelectedDocuments.keys()); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 396fe12b5..f8e77d90a 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -364,8 +364,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> this.Interacting = false; (e.button === 0) && this._resizeUndo?.end(); this._resizeUndo = undefined; - MainView.Instance._hLines = []; - MainView.Instance._vLines = []; + DragManager.Vals.Instance.clearSnapLines(); } @computed @@ -404,7 +403,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const darkScheme = Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimgray" : undefined; const bounds = this.Bounds; const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined; - if (SelectionManager.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { + if (DragManager.Vals.Instance.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { return (null); } const minimal = bounds.r - bounds.x < 100 ? true : false; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index a29a6baac..71c2bf245 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -43,6 +43,7 @@ import PDFMenu from './pdf/PDFMenu'; import { PreviewCursor } from './PreviewCursor'; import { ScriptField } from '../../new_fields/ScriptField'; import { TimelineMenu } from './animationtimeline/TimelineMenu'; +import { DragManager } from '../util/DragManager'; @observer export class MainView extends React.Component { @@ -574,9 +575,6 @@ export class MainView extends React.Component { return this._mainViewRef; } - @observable public _hLines: any; - @observable public _vLines: any; - render() { return (
@@ -597,8 +595,8 @@ export class MainView extends React.Component { {// TO VIEW SNAP LINES
- {this._hLines?.map((l: any) => )} - {this._vLines?.map((l: any) => )} + {DragManager.Vals.Instance.horizSnapLines.map((l: any) => )} + {DragManager.Vals.Instance.vertSnapLines.map((l: any) => )}
} diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 822f44f5a..33ece13cc 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -68,10 +68,11 @@ export class CollectionDockingView extends React.Component { let config: any; if (dragDocs.length === 1) { config = CollectionDockingView.makeDocumentConfig(dragDocs[0]); @@ -499,7 +500,7 @@ export class CollectionDockingView extends React.Component { - if (!this._isPointerDown || !SelectionManager.GetIsDragging()) return; + if (!this._isPointerDown || !DragManager.Vals.Instance.GetIsDragging()) return; const activeContentItem = tab.header.parent.getActiveContentItem(); if (tab.contentItem !== activeContentItem) { tab.header.parent.setActiveContentItem(tab.contentItem); diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 7ad15ef41..c74cfbcf4 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -132,7 +132,7 @@ export class CollectionMasonryViewFieldRow extends React.Component SelectionManager.GetIsDragging() && (this._background = "#b4b4b4")); + pointerEnteredRow = action(() => DragManager.Vals.Instance.GetIsDragging() && (this._background = "#b4b4b4")); @action pointerLeaveRow = () => { diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index 8b8cbc6e8..0a10c24b3 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -12,6 +12,7 @@ import React = require("react"); import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../../Utils"; import { SelectionManager } from "../../util/SelectionManager"; import { UndoManager } from "../../util/UndoManager"; +import { DragManager } from "../../util/DragManager"; @observer export class CollectionPileView extends CollectionSubView(doc => doc) { @@ -78,7 +79,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { _undoBatch: UndoManager.Batch | undefined; pointerDown = (e: React.PointerEvent) => { let dist = 0; - SelectionManager.SetIsDragging(true); + DragManager.Vals.Instance.SetIsDragging(true); // this._lastTap should be set to 0, and this._doubleTap should be set to false in the class header setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { if (this.layoutEngine() === "pass" && this.childDocs.length && this.props.isSelected(true)) { @@ -98,7 +99,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { }, () => { this._undoBatch?.end(); this._undoBatch = undefined; - SelectionManager.SetIsDragging(false); + DragManager.Vals.Instance.SetIsDragging(false); if (!this.childDocs.length) { this.props.ContainingCollectionView?.removeDocument(this.props.Document); } diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 82204ca7b..0e6489947 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -189,7 +189,7 @@ export class CollectionSchemaCell extends React.Component { this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e); }; const onPointerEnter = (e: React.PointerEvent): void => { - if (e.buttons === 1 && SelectionManager.GetIsDragging() && (type === "document" || type === undefined)) { + if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging() && (type === "document" || type === undefined)) { dragRef.current!.className = "collectionSchemaView-cellContainer doc-drag-over"; } }; diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx index 972714e34..f9cd9a628 100644 --- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx +++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx @@ -32,7 +32,7 @@ export class MovableColumn extends React.Component { private _dragRef: React.RefObject = React.createRef(); onPointerEnter = (e: React.PointerEvent): void => { - if (e.buttons === 1 && SelectionManager.GetIsDragging()) { + if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging()) { this._header!.current!.className = "collectionSchema-col-wrapper"; document.addEventListener("pointermove", this.onDragMove, true); } @@ -143,7 +143,7 @@ export class MovableRow extends React.Component { private _rowDropDisposer?: DragManager.DragDropDisposer; onPointerEnter = (e: React.PointerEvent): void => { - if (e.buttons === 1 && SelectionManager.GetIsDragging()) { + if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging()) { this._header!.current!.className = "collectionSchema-row-wrapper"; document.addEventListener("pointermove", this.onDragMove, true); } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 1fd5c3f44..f6cdebc9b 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -312,7 +312,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) this.refList.push(ref); const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc; this.observer = new _global.ResizeObserver(action((entries: any) => { - if (this.props.Document._autoHeight && ref && this.refList.length && !SelectionManager.GetIsDragging()) { + if (this.props.Document._autoHeight && ref && this.refList.length && !DragManager.Vals.Instance.GetIsDragging()) { Doc.Layout(doc)._height = Math.min(1200, Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", ""))))); } })); @@ -359,7 +359,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) this.refList.push(ref); const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc; this.observer = new _global.ResizeObserver(action((entries: any) => { - if (this.props.Document._autoHeight && ref && this.refList.length && !SelectionManager.GetIsDragging()) { + if (this.props.Document._autoHeight && ref && this.refList.length && !DragManager.Vals.Instance.GetIsDragging()) { Doc.Layout(doc)._height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0); } })); diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 8a9539eb0..1d16a5478 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -120,7 +120,7 @@ export class CollectionStackingViewFieldColumn extends React.Component { - if (SelectionManager.GetIsDragging()) { + if (DragManager.Vals.Instance.GetIsDragging()) { this._background = "#b4b4b4"; } } @@ -355,7 +355,7 @@ export class CollectionStackingViewFieldColumn extends React.Component diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 6d6f8d316..288fa8794 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -147,7 +147,7 @@ class TreeView extends React.Component { onPointerEnter = (e: React.PointerEvent): void => { this.props.active(true) && Doc.BrushDoc(this.dataDoc); - if (e.buttons === 1 && SelectionManager.GetIsDragging()) { + if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging()) { this._header!.current!.className = "treeViewItem-header"; document.addEventListener("pointermove", this.onDragMove, true); } @@ -451,7 +451,7 @@ class TreeView extends React.Component { fontWeight: this.props.document.searchMatch ? "bold" : undefined, textDecoration: Doc.GetT(this.props.document, "title", "string", true) ? "underline" : undefined, outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined, - pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? undefined : "none" + pointerEvents: this.props.active() || DragManager.Vals.Instance.GetIsDragging() ? undefined : "none" }} > {Doc.GetT(this.props.document, "editTitle", "boolean", true) ? this.editableView("title") : diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 7df1e909f..0873fd1bb 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -10,6 +10,7 @@ import React = require("react"); import { Utils, emptyFunction } from "../../../../Utils"; import { SelectionManager } from "../../../util/SelectionManager"; import { DocumentType } from "../../../documents/DocumentTypes"; +import { DragManager } from "../../../util/DragManager"; @observer export class CollectionFreeFormLinksView extends React.Component { @@ -36,7 +37,7 @@ export class CollectionFreeFormLinksView extends React.Component { } render() { - return SelectionManager.GetIsDragging() ? (null) :
+ return DragManager.Vals.Instance.GetIsDragging() ? (null) :
{this.uniqueConnections} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 411fbd9e2..b2401e69e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1174,7 +1174,7 @@ export class CollectionFreeFormView extends CollectionSubView { - if (SelectionManager.GetIsDragging()) { + if (DragManager.Vals.Instance.GetIsDragging()) { this.setupDragLines(); } e.stopPropagation(); @@ -1233,7 +1233,7 @@ export class CollectionFreeFormView extends CollectionSubView { document.removeEventListener("pointerup", this.onLinkButtonUp); const targets = this.props.group.map(l => LinkManager.Instance.getOppositeAnchor(l, this.props.sourceDoc)).filter(d => d) as Doc[]; - DragManager.StartLinkTargetsDrag(this._drag.current, this.props.docView, e.x, e.y, this.props.sourceDoc, targets); + StartLinkTargetsDrag(this._drag.current, this.props.docView, e.x, e.y, this.props.sourceDoc, targets); } e.stopPropagation(); } diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index d091e06ef..d4e58fa8e 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -3,7 +3,7 @@ import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes } from import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, observable } from 'mobx'; import { observer } from "mobx-react"; -import { Doc } from '../../../new_fields/Doc'; +import { Doc, DocListCast } from '../../../new_fields/Doc'; import { Cast, StrCast } from '../../../new_fields/Types'; import { DragManager } from '../../util/DragManager'; import { LinkManager } from '../../util/LinkManager'; @@ -26,6 +26,41 @@ interface LinkMenuItemProps { addDocTab: (document: Doc, where: string) => boolean; } +// drag links and drop link targets (aliasing them if needed) +export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: DocumentView, downX: number, downY: number, sourceDoc: Doc, specificLinks?: Doc[]) { + const draggedDocs = (specificLinks ? specificLinks : DocListCast(sourceDoc.links)).map(link => LinkManager.Instance.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[]; + + if (draggedDocs.length) { + const moddrag: Doc[] = []; + for (const draggedDoc of draggedDocs) { + const doc = await Cast(draggedDoc.annotationOn, Doc); + if (doc) moddrag.push(doc); + } + + const dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs); + dragData.moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (doc: Doc) => boolean): boolean => { + docView.props.removeDocument?.(doc); + addDocument(doc); + return true; + }; + const containingView = docView.props.ContainingCollectionView; + const finishDrag = (e: DragManager.DragCompleteEvent) => + e.docDragData && (e.docDragData.droppedDocuments = + dragData.draggedDocuments.reduce((droppedDocs, d) => { + const dvs = DocumentManager.Instance.getDocumentViews(d).filter(dv => dv.props.ContainingCollectionView === containingView); + if (dvs.length) { + dvs.forEach(dv => droppedDocs.push(dv.props.Document)); + } else { + droppedDocs.push(Doc.MakeAlias(d)); + } + return droppedDocs; + }, [] as Doc[])); + + DragManager.StartDrag([dragEle], dragData, downX, downY, undefined, finishDrag); + } +} + + @observer export class LinkMenuItem extends React.Component { private _drag = React.createRef(); @@ -83,7 +118,7 @@ export class LinkMenuItem extends React.Component { document.removeEventListener("pointerup", this.onLinkButtonUp); this._eleClone.style.transform = `translate(${e.x}px, ${e.y}px)`; - DragManager.StartLinkTargetsDrag(this._eleClone, this.props.docView, e.x, e.y, this.props.sourceDoc, [this.props.linkDoc]); + StartLinkTargetsDrag(this._eleClone, this.props.docView, e.x, e.y, this.props.sourceDoc, [this.props.linkDoc]); } e.stopPropagation(); } diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 3feb533a0..1c5e13620 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -17,10 +17,9 @@ import { ContextMenu } from "../ContextMenu"; import { Id } from "../../../new_fields/FieldSymbols"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { DocumentView } from "./DocumentView"; -import { Docs } from "../../documents/Documents"; +import { Docs, DocUtils } from "../../documents/Documents"; import { ComputedField } from "../../../new_fields/ScriptField"; import { Networking } from "../../Network"; -import { Upload } from "../../../server/SharedMediaTypes"; import { LinkAnchorBox } from "./LinkAnchorBox"; // testing testing @@ -57,7 +56,6 @@ export class AudioBox extends ViewBoxBaseComponent { runInAction(() => AudioBox._scrubTime = 0); runInAction(() => AudioBox._scrubTime = timeInMillisFrom1970); }; - public static ActiveRecordings: Doc[] = []; @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); } async slideTemplate() { return (await Cast((await Cast(Doc.UserDoc().slidesBtn, Doc) as Doc).dragFactory, Doc) as Doc); } @@ -145,7 +143,7 @@ export class AudioBox extends ViewBoxBaseComponent { const [{ result }] = await Networking.UploadFilesToServer(e.data); if (!(result instanceof Error)) { @@ -172,8 +170,8 @@ export class AudioBox extends ViewBoxBaseComponent { diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx index 3c2c6c87e..1c6250b94 100644 --- a/src/client/views/nodes/ContentFittingDocumentView.tsx +++ b/src/client/views/nodes/ContentFittingDocumentView.tsx @@ -3,7 +3,7 @@ import { computed } from "mobx"; import { observer } from "mobx-react"; import "react-table/react-table.css"; import { Doc, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc"; -import { NumCast, StrCast } from "../../../new_fields/Types"; +import { NumCast, StrCast, Cast } from "../../../new_fields/Types"; import { TraceMobx } from "../../../new_fields/util"; import { emptyFunction, returnOne } from "../../../Utils"; import '../DocumentDecorations.scss'; @@ -14,7 +14,11 @@ import "./ContentFittingDocumentView.scss"; @observer export class ContentFittingDocumentView extends React.Component{ public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive - private get layoutDoc() { return this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document); } + private get layoutDoc() { + return this.props.LayoutTemplate?.() || + (this.props.layoutKey && Doc.Layout(this.props.Document, Cast(this.props.Document[this.props.layoutKey], Doc, null))) || + Doc.Layout(this.props.Document); + } @computed get freezeDimensions() { return this.props.FreezeDimensions; } nativeWidth = () => NumCast(this.layoutDoc?._nativeWidth, this.props.NativeWidth?.() || (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[WidthSym]() : this.props.PanelWidth())); nativeHeight = () => NumCast(this.layoutDoc?._nativeHeight, this.props.NativeHeight?.() || (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[HeightSym]() : this.props.PanelHeight())); diff --git a/src/client/views/nodes/DocumentBox.scss b/src/client/views/nodes/DocumentBox.scss index ce21391ce..3a27c16c1 100644 --- a/src/client/views/nodes/DocumentBox.scss +++ b/src/client/views/nodes/DocumentBox.scss @@ -2,7 +2,8 @@ width: 100%; height: 100%; pointer-events: all; - background: gray; + background: rgb(241, 239, 235); + position: absolute; .documentBox-lock { margin: auto; color: white; diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx index 8d422fe67..b53c7cfe6 100644 --- a/src/client/views/nodes/DocumentBox.tsx +++ b/src/client/views/nodes/DocumentBox.tsx @@ -1,23 +1,24 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { IReactionDisposer, reaction, computed } from "mobx"; +import { action, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; import { Doc, Field } from "../../../new_fields/Doc"; -import { documentSchema, collectionSchema } from "../../../new_fields/documentSchemas"; +import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas"; import { makeInterface } from "../../../new_fields/Schema"; import { ComputedField } from "../../../new_fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { TraceMobx } from "../../../new_fields/util"; import { emptyPath, returnFalse, returnOne, returnZero } from "../../../Utils"; +import { DocumentType } from "../../documents/DocumentTypes"; +import { DragManager } from "../../util/DragManager"; +import { undoBatch } from "../../util/UndoManager"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; import { ViewBoxAnnotatableComponent } from "../DocComponent"; import { ContentFittingDocumentView } from "./ContentFittingDocumentView"; import "./DocumentBox.scss"; +import { DocumentView } from "./DocumentView"; import { FieldView, FieldViewProps } from "./FieldView"; import React = require("react"); -import { TraceMobx } from "../../../new_fields/util"; -import { Docs } from "../../documents/Documents"; -import { KeyValueBox } from "./KeyValueBox"; -import { DocumentView } from "./DocumentView"; type DocHolderBoxSchema = makeInterface<[typeof documentSchema, typeof collectionSchema]>; const DocHolderBoxDocument = makeInterface(documentSchema, collectionSchema); @@ -26,7 +27,9 @@ const DocHolderBoxDocument = makeInterface(documentSchema, collectionSchema); export class DocHolderBox extends ViewBoxAnnotatableComponent(DocHolderBoxDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocHolderBox, fieldKey); } _prevSelectionDisposer: IReactionDisposer | undefined; + _dropDisposer?: DragManager.DragDropDisposer; _selections: Doc[] = []; + _contRef = React.createRef(); _curSelection = -1; componentDidMount() { this._prevSelectionDisposer = reaction(() => this.layoutDoc[this.props.fieldKey], (data) => { @@ -101,21 +104,15 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent(); pwidth = () => this.props.PanelWidth() - 2 * this.xPad; pheight = () => this.props.PanelHeight() - 2 * this.yPad; getTransform = () => this.props.ScreenToLocalTransform().translate(-this.xPad, -this.yPad); isActive = () => this.active() || !this.props.renderDepth; + layoutTemplateDoc = () => Cast(this.props.Document.childLayoutTemplate, Doc, null); get renderContents() { const containedDoc = Cast(this.dataDoc[this.props.fieldKey], Doc, null); - const childTemplateName = StrCast(this.layoutDoc.childTemplateName); - if (containedDoc && childTemplateName && !containedDoc["layout_" + childTemplateName]) { - setTimeout(() => { - Doc.createCustomView(containedDoc, Docs.Create.StackingDocument, childTemplateName); - Doc.expandTemplateLayout(Cast(containedDoc["layout_" + childTemplateName], Doc, null), containedDoc, undefined); - }, 0); - } - const contents = !(containedDoc instanceof Doc) ? (null) : this.layoutDoc.childLayoutString ? + const layoutTemplate = StrCast(this.layoutDoc.childLayoutString); + const contents = !(containedDoc instanceof Doc) ? (null) : this.layoutDoc.childLayoutString || this.layoutTemplateDoc() ? : @@ -150,8 +147,8 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent {this.renderContents} -
; } + + @undoBatch + @action + drop = (e: Event, de: DragManager.DropEvent) => { + if (de.complete.docDragData) { + if (de.complete.docDragData.draggedDocuments[0].type === DocumentType.FONTICON) { + const doc = Cast(de.complete.docDragData.draggedDocuments[0].dragFactory, Doc, null); + this.props.Document.childLayoutTemplate = doc; + } + } + } + protected createDropTarget = (ele: HTMLDivElement) => { + this._dropDisposer?.(); + ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document)); + } + } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7a16e8836..c4cd5978a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1102,7 +1102,7 @@ export class DocumentView extends DocComponent(Docu } @computed get ignorePointerEvents() { return this.props.pointerEvents === false || - (this.Document.isBackground && !this.isSelected() && !SelectionManager.GetIsDragging()) || + (this.Document.isBackground && !this.isSelected() && !DragManager.Vals.Instance.GetIsDragging()) || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None); } @undoBatch diff --git a/src/client/views/nodes/QueryBox.tsx b/src/client/views/nodes/QueryBox.tsx index 76885eada..d248b098c 100644 --- a/src/client/views/nodes/QueryBox.tsx +++ b/src/client/views/nodes/QueryBox.tsx @@ -11,6 +11,7 @@ import { SearchBox } from "../search/SearchBox"; import { FieldView, FieldViewProps } from './FieldView'; import "./QueryBox.scss"; import { List } from "../../../new_fields/List"; +import { DragManager } from "../../util/DragManager"; type QueryDocument = makeInterface<[typeof documentSchema]>; const QueryDocument = makeInterface(documentSchema); @@ -27,7 +28,7 @@ export class QueryBox extends ViewBoxAnnotatableComponent e.stopPropagation()} > m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 1000))); const curText = state.doc.textBetween(0, state.doc.content.size, " \n"); - const curTemp = Cast(this.props.Document[this.props.fieldKey + "-textTemplate"], RichTextField); - const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); + const curTemp = Cast(this.props.Document[this.props.fieldKey + "-textTemplate"], RichTextField); // the actual text in the text box + const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype + const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template const json = JSON.stringify(state.toJSON()); if (!this._applyingChange && json.replace(/"selection":.*/, "") !== curProto?.Data.replace(/"selection":.*/, "")) { this._applyingChange = true; this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); - if ((!curTemp && !curProto) || curText) { // if no template, or there's text, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended) - this.dataDoc[this.props.fieldKey] = new RichTextField(json, curText); - this.dataDoc[this.props.fieldKey + "-noTemplate"] = (curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited + if ((!curTemp && !curProto) || curText) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended) + if (curText !== curLayout?.Text) { + this.dataDoc[this.props.fieldKey] = new RichTextField(json, curText); + this.dataDoc[this.props.fieldKey + "-noTemplate"] = (curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited + } } else { // if we've deleted all the text in a note driven by a template, then restore the template data this.dataDoc[this.props.fieldKey] = undefined; this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse((curProto || curTemp).Data))); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index b63a3dbbf..bf99f449f 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -217,28 +217,29 @@ export class CurrentUserUtils { static setupDefaultIconTemplates(doc: Doc) { if (doc["template-icon-view"] === undefined) { const iconView = Docs.Create.TextDocument("", { - title: "icon", _width: 150, _height: 30, isTemplateDoc: true, - onClick: ScriptField.MakeScript("deiconifyView(self)") + title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") }); Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', ""); iconView.isTemplateDoc = makeTemplate(iconView); doc["template-icon-view"] = new PrefetchProxy(iconView); } if (doc["template-icon-view-rtf"] === undefined) { - const iconRtfView = Docs.Create.LabelDocument({ title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(self)") }); + const iconRtfView = Docs.Create.LabelDocument({ + title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", + _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") + }); iconRtfView.isTemplateDoc = makeTemplate(iconRtfView, true, "icon_" + DocumentType.RTF); doc["template-icon-view-rtf"] = new PrefetchProxy(iconRtfView); } if (doc["template-icon-view-img"] === undefined) { const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { - title: "data", _width: 50, isTemplateDoc: true, - onClick: ScriptField.MakeScript("deiconifyView(self)") + title: "data", _width: 50, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") }); iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG); doc["template-icon-view-img"] = new PrefetchProxy(iconImageView); } if (doc["template-icon-view-col"] === undefined) { - const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onClick: ScriptField.MakeScript("deiconifyView(self)") }); + const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") }); iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL); doc["template-icon-view-col"] = new PrefetchProxy(iconColView); } @@ -265,9 +266,17 @@ export class CurrentUserUtils { doc.emptyCollection = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _LODdisable: true, _width: 150, _height: 100, title: "freeform" }); } + if (doc.emptyDocHolder === undefined) { + doc.emptyDocHolder = Docs.Create.DocumentDocument( + ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]") as any, + { _width: 250, _height: 250, title: "container" }); + } + if (doc.emptyWebpage === undefined) { + doc.emptyWebpage = Docs.Create.WebDocument("", { title: "New Webpage", _width: 600 }) + } return [ { title: "Drag a collection", label: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyCollection as Doc }, - { title: "Drag a web page", label: "Web", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("", { title: "New Webpage" })' }, + { title: "Drag a web page", label: "Web", icon: "globe-asia", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyWebpage as Doc }, { title: "Drag a cat image", label: "Img", icon: "cat", ignoreClick: true, drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth:250, title: "an image of a cat" })' }, { title: "Drag a screenshot", label: "Grab", icon: "photo-video", ignoreClick: true, drag: 'Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot" })' }, { title: "Drag a webcam", label: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' }, @@ -284,7 +293,7 @@ export class CurrentUserUtils { // { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc }, // { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "pink", activePen: doc }, // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.inkPen = this;', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "white", activePen: doc }, - { title: "Drag a document previewer", label: "Prev", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' }, + { title: "Drag a document previewer", label: "Prev", icon: "expand", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory,true)', dragFactory: doc.emptyDocHolder as Doc }, { title: "Drag a Calculator REPL", label: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' }, ]; @@ -308,7 +317,7 @@ export class CurrentUserUtils { title: data.title, label: data.label, ignoreClick: data.ignoreClick, - dropAction: data.click ? "copy" : undefined, + dropAction: "copy", onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, -- cgit v1.2.3-70-g09d2 From d9227908ba8e8db5084c468a242b2839ab11a33d Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 4 May 2020 12:50:46 -0400 Subject: fixed links and snap lines broken by moving things into DragManager. --- src/client/documents/Documents.ts | 2 +- src/client/util/DragManager.ts | 34 ++-- src/client/util/SelectionManager.ts | 3 + src/client/util/SnappingManager.ts | 29 +++ src/client/views/DocumentDecorations.tsx | 5 +- src/client/views/MainView.scss | 9 + src/client/views/MainView.tsx | 18 +- .../views/collections/CollectionDockingView.tsx | 5 +- .../collections/CollectionMasonryViewFieldRow.tsx | 4 +- .../views/collections/CollectionPileView.tsx | 6 +- .../views/collections/CollectionSchemaCells.tsx | 4 +- .../CollectionSchemaMovableTableHOC.tsx | 6 +- .../views/collections/CollectionStackingView.tsx | 7 +- .../CollectionStackingViewFieldColumn.tsx | 6 +- .../views/collections/CollectionTreeView.tsx | 7 +- .../CollectionFreeFormLinkView.tsx | 9 +- .../CollectionFreeFormLinksView.tsx | 4 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 7 +- src/client/views/nodes/DocHolderBox.scss | 15 ++ src/client/views/nodes/DocHolderBox.tsx | 207 +++++++++++++++++++++ src/client/views/nodes/DocumentBox.scss | 15 -- src/client/views/nodes/DocumentBox.tsx | 207 --------------------- src/client/views/nodes/DocumentContentsView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 3 +- src/client/views/nodes/QueryBox.tsx | 5 +- 25 files changed, 328 insertions(+), 291 deletions(-) create mode 100644 src/client/util/SnappingManager.ts create mode 100644 src/client/views/nodes/DocHolderBox.scss create mode 100644 src/client/views/nodes/DocHolderBox.tsx delete mode 100644 src/client/views/nodes/DocumentBox.scss delete mode 100644 src/client/views/nodes/DocumentBox.tsx (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 45687f597..f5d6cd7f6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -37,7 +37,7 @@ import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; import { QueryBox } from "../views/nodes/QueryBox"; import { ColorBox } from "../views/nodes/ColorBox"; import { LinkAnchorBox } from "../views/nodes/LinkAnchorBox"; -import { DocHolderBox } from "../views/nodes/DocumentBox"; +import { DocHolderBox } from "../views/nodes/DocHolderBox"; import { InkingStroke } from "../views/InkingStroke"; import { InkField } from "../../new_fields/InkField"; import { InkingControl } from "../views/InkingControl"; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 348aba588..0d208cf1b 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -11,6 +11,7 @@ import { emptyFunction } from "../../Utils"; import { Docs, DocUtils } from "../documents/Documents"; import * as globalCssVariables from "../views/globalCssVariables.scss"; import { UndoManager } from "./UndoManager"; +import { SnappingManager } from "./SnappingManager"; export type dropActionType = "alias" | "copy" | "move" | undefined; // undefined = move export function SetupDrag( @@ -49,7 +50,7 @@ export function SetupDrag( if (e.shiftKey) { e.persist(); const dragDoc = await docFunc(); - dragDoc && DragManager.Vals.Instance.StartWindowDrag?.({ + dragDoc && DragManager.StartWindowDrag?.({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, @@ -66,19 +67,7 @@ export function SetupDrag( export namespace DragManager { let dragDiv: HTMLDivElement; - export class Vals { - static Instance: Vals = new Vals(); - @observable public IsDragging: boolean = false; - @observable public horizSnapLines: number[] = []; - @observable public vertSnapLines: number[] = []; - public StartWindowDrag: Opt<((e: any, dragDocs: Doc[]) => void)> = undefined; - public SetIsDragging(dragging: boolean) { runInAction(() => this.IsDragging = dragging); } - public GetIsDragging() { return this.IsDragging; } - @action public clearSnapLines() { - this.vertSnapLines.length = 0; - this.horizSnapLines.length = 0; - } - } + export let StartWindowDrag: Opt<((e: any, dragDocs: Doc[]) => void)> = undefined; export function Root() { const root = document.getElementById("root"); @@ -267,8 +256,7 @@ export namespace DragManager { } export function SetSnapLines(horizLines: number[], vertLines: number[]) { - DragManager.Vals.Instance.horizSnapLines.push(...horizLines); - DragManager.Vals.Instance.vertSnapLines.push(...vertLines); + SnappingManager.setSnapLines(horizLines, vertLines); } export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) { @@ -286,8 +274,8 @@ export namespace DragManager { }; return { - thisX: snapVal([xFromLeft, xFromRight], e.pageX, DragManager.Vals.Instance.vertSnapLines), - thisY: snapVal([yFromTop, yFromBottom], e.pageY, DragManager.Vals.Instance.horizSnapLines) + thisX: snapVal([xFromLeft, xFromRight], e.pageX, SnappingManager.vertSnapLines()), + thisY: snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.horizSnapLines()) }; } export let docsBeingDragged: Doc[] = []; @@ -299,7 +287,7 @@ export namespace DragManager { dragDiv.style.pointerEvents = "none"; DragManager.Root().appendChild(dragDiv); } - DragManager.Vals.Instance.SetIsDragging(true); + SnappingManager.SetIsDragging(true); const scaleXs: number[] = []; const scaleYs: number[] = []; const xs: number[] = []; @@ -388,7 +376,7 @@ export namespace DragManager { } AbortDrag(); finishDrag?.(new DragCompleteEvent(true, dragData)); - DragManager.Vals.Instance.StartWindowDrag?.({ + DragManager.StartWindowDrag?.({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, @@ -415,19 +403,19 @@ export namespace DragManager { const endDrag = action(() => { document.removeEventListener("pointermove", moveHandler, true); document.removeEventListener("pointerup", upHandler); - DragManager.Vals.Instance.clearSnapLines(); + SnappingManager.clearSnapLines(); }); AbortDrag = () => { hideDragShowOriginalElements(); - DragManager.Vals.Instance.SetIsDragging(false); + SnappingManager.SetIsDragging(false); options?.dragComplete?.(new DragCompleteEvent(true, dragData)); endDrag(); }; const upHandler = (e: PointerEvent) => { hideDragShowOriginalElements(); dispatchDrag(eles, e, dragData, xFromLeft, yFromTop, xFromRight, yFromBottom, options, finishDrag); - DragManager.Vals.Instance.SetIsDragging(false); + SnappingManager.SetIsDragging(false); endDrag(); options?.dragComplete?.(new DragCompleteEvent(false, dragData)); }; diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index d509168b6..11d2cafb2 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -8,6 +8,7 @@ export namespace SelectionManager { class Manager { + @observable IsDragging: boolean = false; SelectedDocuments: ObservableMap = new ObservableMap(); @action @@ -53,6 +54,8 @@ export namespace SelectionManager { manager.SelectDoc(docView, ctrlPressed); } + export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); } + export function GetIsDragging() { return manager.IsDragging; } // computed functions, such as used in IsSelected generate errors if they're called outside of a // reaction context. Specifying the context with 'outsideReaction' allows an efficiency feature // to avoid unnecessary mobx invalidations when running inside a reaction. diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts new file mode 100644 index 000000000..fc07e8ab4 --- /dev/null +++ b/src/client/util/SnappingManager.ts @@ -0,0 +1,29 @@ +import { observable, action, runInAction } from "mobx"; + +export namespace SnappingManager { + + class Manager { + @observable IsDragging: boolean = false; + @observable public horizSnapLines: number[] = []; + @observable public vertSnapLines: number[] = []; + @action public clearSnapLines() { + this.vertSnapLines = []; + this.horizSnapLines = []; + } + @action public setSnapLines(horizLines: number[], vertLines: number[]) { + this.horizSnapLines = horizLines; + this.vertSnapLines = vertLines; + } + } + + const manager = new Manager(); + + export function clearSnapLines() { manager.clearSnapLines(); } + export function setSnapLines(horizLines: number[], vertLines: number[]) { manager.setSnapLines(horizLines, vertLines); } + export function horizSnapLines() { return manager.horizSnapLines; } + export function vertSnapLines() { return manager.vertSnapLines; } + + export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); } + export function GetIsDragging() { return manager.IsDragging; } +} + diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index f8e77d90a..b89806656 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -21,6 +21,7 @@ import { Id } from '../../new_fields/FieldSymbols'; import e = require('express'); import { CollectionDockingView } from './collections/CollectionDockingView'; import { MainView } from './MainView'; +import { SnappingManager } from '../util/SnappingManager'; library.add(faCaretUp); library.add(faObjectGroup); @@ -364,7 +365,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> this.Interacting = false; (e.button === 0) && this._resizeUndo?.end(); this._resizeUndo = undefined; - DragManager.Vals.Instance.clearSnapLines(); + SnappingManager.clearSnapLines(); } @computed @@ -403,7 +404,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const darkScheme = Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimgray" : undefined; const bounds = this.Bounds; const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined; - if (DragManager.Vals.Instance.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { + if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { return (null); } const minimal = bounds.r - bounds.x < 100 ? true : false; diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 04288a9e1..753ba700c 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -22,6 +22,15 @@ z-index: 1; } +.mainView-snapLines { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events:none; +} + .mainView-container, .mainView-container-dark { input { color: unset !important; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 71c2bf245..9ebd60c52 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -44,6 +44,7 @@ import { PreviewCursor } from './PreviewCursor'; import { ScriptField } from '../../new_fields/ScriptField'; import { TimelineMenu } from './animationtimeline/TimelineMenu'; import { DragManager } from '../util/DragManager'; +import { SnappingManager } from '../util/SnappingManager'; @observer export class MainView extends React.Component { @@ -575,6 +576,15 @@ export class MainView extends React.Component { return this._mainViewRef; } + @computed get snapLines() { + return
+ + {SnappingManager.horizSnapLines().map(l => )} + {SnappingManager.vertSnapLines().map(l => )} + +
+ } + render() { return (
@@ -592,14 +602,8 @@ export class MainView extends React.Component { - {// TO VIEW SNAP LINES -
- - {DragManager.Vals.Instance.horizSnapLines.map((l: any) => )} - {DragManager.Vals.Instance.vertSnapLines.map((l: any) => )} - -
} + {this.snapLines}
); } } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 33ece13cc..6cd5d1b1b 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -30,6 +30,7 @@ import { SubCollectionViewProps } from "./CollectionSubView"; import { DockingViewButtonSelector } from './ParentDocumentSelector'; import React = require("react"); import { CollectionViewType } from './CollectionView'; +import { SnappingManager } from '../../util/SnappingManager'; library.add(faFile); const _global = (window /* browser */ || global /* node */) as any; @@ -68,7 +69,7 @@ export class CollectionDockingView extends React.Component { - if (!this._isPointerDown || !DragManager.Vals.Instance.GetIsDragging()) return; + if (!this._isPointerDown || !SnappingManager.GetIsDragging()) return; const activeContentItem = tab.header.parent.getActiveContentItem(); if (tab.contentItem !== activeContentItem) { tab.header.parent.setActiveContentItem(tab.contentItem); diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index c74cfbcf4..95c7643c9 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -12,12 +12,12 @@ import { numberRange, setupMoveUpEvents, emptyFunction } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { CompileScript } from "../../util/Scripting"; -import { SelectionManager } from "../../util/SelectionManager"; import { Transform } from "../../util/Transform"; import { undoBatch } from "../../util/UndoManager"; import { EditableView } from "../EditableView"; import { CollectionStackingView } from "./CollectionStackingView"; import "./CollectionStackingView.scss"; +import { SnappingManager } from "../../util/SnappingManager"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -132,7 +132,7 @@ export class CollectionMasonryViewFieldRow extends React.Component DragManager.Vals.Instance.GetIsDragging() && (this._background = "#b4b4b4")); + pointerEnteredRow = action(() => SnappingManager.GetIsDragging() && (this._background = "#b4b4b4")); @action pointerLeaveRow = () => { diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index 0a10c24b3..d3ae21f3a 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -12,7 +12,7 @@ import React = require("react"); import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../../Utils"; import { SelectionManager } from "../../util/SelectionManager"; import { UndoManager } from "../../util/UndoManager"; -import { DragManager } from "../../util/DragManager"; +import { SnappingManager } from "../../util/SnappingManager"; @observer export class CollectionPileView extends CollectionSubView(doc => doc) { @@ -79,7 +79,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { _undoBatch: UndoManager.Batch | undefined; pointerDown = (e: React.PointerEvent) => { let dist = 0; - DragManager.Vals.Instance.SetIsDragging(true); + SnappingManager.SetIsDragging(true); // this._lastTap should be set to 0, and this._doubleTap should be set to false in the class header setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { if (this.layoutEngine() === "pass" && this.childDocs.length && this.props.isSelected(true)) { @@ -99,7 +99,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { }, () => { this._undoBatch?.end(); this._undoBatch = undefined; - DragManager.Vals.Instance.SetIsDragging(false); + SnappingManager.SetIsDragging(false); if (!this.childDocs.length) { this.props.ContainingCollectionView?.removeDocument(this.props.Document); } diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 0e6489947..5253ee0b9 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -18,11 +18,11 @@ import "./CollectionSchemaView.scss"; import { CollectionView } from "./CollectionView"; import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../new_fields/Types"; import { Docs } from "../../documents/Documents"; -import { SelectionManager } from "../../util/SelectionManager"; import { library } from '@fortawesome/fontawesome-svg-core'; import { faExpand } from '@fortawesome/free-solid-svg-icons'; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import { undoBatch } from "../../util/UndoManager"; +import { SnappingManager } from "../../util/SnappingManager"; library.add(faExpand); @@ -189,7 +189,7 @@ export class CollectionSchemaCell extends React.Component { this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e); }; const onPointerEnter = (e: React.PointerEvent): void => { - if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging() && (type === "document" || type === undefined)) { + if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === "document" || type === undefined)) { dragRef.current!.className = "collectionSchemaView-cellContainer doc-drag-over"; } }; diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx index f9cd9a628..8636e3eb5 100644 --- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx +++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx @@ -4,7 +4,6 @@ import "./CollectionSchemaView.scss"; import { Transform } from "../../util/Transform"; import { Doc } from "../../../new_fields/Doc"; import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager"; -import { SelectionManager } from "../../util/SelectionManager"; import { Cast, FieldValue, StrCast } from "../../../new_fields/Types"; import { ContextMenu } from "../ContextMenu"; import { action } from "mobx"; @@ -14,6 +13,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { DocumentManager } from "../../util/DocumentManager"; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import { undoBatch } from "../../util/UndoManager"; +import { SnappingManager } from "../../util/SnappingManager"; library.add(faGripVertical, faTrash); @@ -32,7 +32,7 @@ export class MovableColumn extends React.Component { private _dragRef: React.RefObject = React.createRef(); onPointerEnter = (e: React.PointerEvent): void => { - if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging()) { + if (e.buttons === 1 && SnappingManager.GetIsDragging()) { this._header!.current!.className = "collectionSchema-col-wrapper"; document.addEventListener("pointermove", this.onDragMove, true); } @@ -143,7 +143,7 @@ export class MovableRow extends React.Component { private _rowDropDisposer?: DragManager.DragDropDisposer; onPointerEnter = (e: React.PointerEvent): void => { - if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging()) { + if (e.buttons === 1 && SnappingManager.GetIsDragging()) { this._header!.current!.className = "collectionSchema-row-wrapper"; document.addEventListener("pointermove", this.onDragMove, true); } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index f6cdebc9b..b6faea845 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -14,7 +14,6 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_field import { TraceMobx } from "../../../new_fields/util"; import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from "../../../Utils"; import { DragManager, dropActionType } from "../../util/DragManager"; -import { SelectionManager } from "../../util/SelectionManager"; import { Transform } from "../../util/Transform"; import { undoBatch } from "../../util/UndoManager"; import { ContextMenu } from "../ContextMenu"; @@ -26,7 +25,7 @@ import "./CollectionStackingView.scss"; import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn"; import { CollectionSubView } from "./CollectionSubView"; import { CollectionViewType } from "./CollectionView"; -import { ScriptField } from "../../../new_fields/ScriptField"; +import { SnappingManager } from "../../util/SnappingManager"; const _global = (window /* browser */ || global /* node */) as any; type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>; @@ -312,7 +311,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) this.refList.push(ref); const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc; this.observer = new _global.ResizeObserver(action((entries: any) => { - if (this.props.Document._autoHeight && ref && this.refList.length && !DragManager.Vals.Instance.GetIsDragging()) { + if (this.props.Document._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) { Doc.Layout(doc)._height = Math.min(1200, Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", ""))))); } })); @@ -359,7 +358,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) this.refList.push(ref); const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc; this.observer = new _global.ResizeObserver(action((entries: any) => { - if (this.props.Document._autoHeight && ref && this.refList.length && !DragManager.Vals.Instance.GetIsDragging()) { + if (this.props.Document._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) { Doc.Layout(doc)._height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0); } })); diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 1d16a5478..dccef7983 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -13,7 +13,6 @@ import { ImageField } from "../../../new_fields/URLField"; import { TraceMobx } from "../../../new_fields/util"; import { Docs, DocUtils } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; -import { SelectionManager } from "../../util/SelectionManager"; import { Transform } from "../../util/Transform"; import { undoBatch } from "../../util/UndoManager"; import { ContextMenu } from "../ContextMenu"; @@ -23,6 +22,7 @@ import { CollectionStackingView } from "./CollectionStackingView"; import { setupMoveUpEvents, emptyFunction } from "../../../Utils"; import "./CollectionStackingView.scss"; import { listSpec } from "../../../new_fields/Schema"; +import { SnappingManager } from "../../util/SnappingManager"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -120,7 +120,7 @@ export class CollectionStackingViewFieldColumn extends React.Component { - if (DragManager.Vals.Instance.GetIsDragging()) { + if (SnappingManager.GetIsDragging()) { this._background = "#b4b4b4"; } } @@ -355,7 +355,7 @@ export class CollectionStackingViewFieldColumn extends React.Component diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 288fa8794..f2b0e3155 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -6,6 +6,7 @@ import { observer } from "mobx-react"; import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; +import { PrefetchProxy } from '../../../new_fields/Proxy'; import { Document, listSpec } from '../../../new_fields/Schema'; import { ComputedField, ScriptField } from '../../../new_fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types'; @@ -13,6 +14,7 @@ import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZer import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; import { DocumentManager } from '../../util/DocumentManager'; +import { SnappingManager } from '../../util/SnappingManager'; import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager"; import { Scripting } from '../../util/Scripting'; import { SelectionManager } from '../../util/SelectionManager'; @@ -31,7 +33,6 @@ import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import { CollectionViewType } from './CollectionView'; import React = require("react"); -import { PrefetchProxy } from '../../../new_fields/Proxy'; export interface TreeViewProps { @@ -147,7 +148,7 @@ class TreeView extends React.Component { onPointerEnter = (e: React.PointerEvent): void => { this.props.active(true) && Doc.BrushDoc(this.dataDoc); - if (e.buttons === 1 && DragManager.Vals.Instance.GetIsDragging()) { + if (e.buttons === 1 && SnappingManager.GetIsDragging()) { this._header!.current!.className = "treeViewItem-header"; document.addEventListener("pointermove", this.onDragMove, true); } @@ -451,7 +452,7 @@ class TreeView extends React.Component { fontWeight: this.props.document.searchMatch ? "bold" : undefined, textDecoration: Doc.GetT(this.props.document, "title", "string", true) ? "underline" : undefined, outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined, - pointerEvents: this.props.active() || DragManager.Vals.Instance.GetIsDragging() ? undefined : "none" + pointerEvents: this.props.active() || SnappingManager.GetIsDragging() ? undefined : "none" }} > {Doc.GetT(this.props.document, "editTitle", "boolean", true) ? this.editableView("title") : diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 0c9a1aa9a..695898ca9 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -9,7 +9,7 @@ import { DocumentType } from "../../../documents/DocumentTypes"; import { observable, action, reaction, IReactionDisposer } from "mobx"; import { StrCast } from "../../../../new_fields/Types"; import { Id } from "../../../../new_fields/FieldSymbols"; -import { DragManager } from "../../../util/DragManager"; +import { SnappingManager } from "../../../util/SnappingManager"; export interface CollectionFreeFormLinkViewProps { A: DocumentView; @@ -25,7 +25,7 @@ export class CollectionFreeFormLinkView extends React.Component [this.props.A.props.ScreenToLocalTransform(), this.props.B.props.ScreenToLocalTransform(), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document)], action(() => { - if (DragManager.Vals.Instance.GetIsDragging()) return; + if (SnappingManager.GetIsDragging()) return; setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render() setTimeout(action(() => (!this.props.LinkDocs.length || !this.props.LinkDocs[0].linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line. const acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("linkAnchorBox-cont") : []; @@ -83,7 +83,7 @@ export class CollectionFreeFormLinkView extends React.Component - + {text !== "-ungrouped-" ? text : ""} + return SnappingManager.GetIsDragging() ? (null) :
{this.uniqueConnections} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 2aa9b1f5f..37957b0f6 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -45,6 +45,7 @@ import { MarqueeView } from "./MarqueeView"; import React = require("react"); import { CollectionViewType } from "../CollectionView"; import { Timeline } from "../../animationtimeline/Timeline"; +import { SnappingManager } from "../../../util/SnappingManager"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); @@ -1174,7 +1175,7 @@ export class CollectionFreeFormView extends CollectionSubView { - if (DragManager.Vals.Instance.GetIsDragging()) { + if (SnappingManager.GetIsDragging()) { this.setupDragLines(); } e.stopPropagation(); @@ -1233,7 +1234,7 @@ export class CollectionFreeFormView extends CollectionSubView; +const DocHolderBoxDocument = makeInterface(documentSchema, collectionSchema); + +@observer +export class DocHolderBox extends ViewBoxAnnotatableComponent(DocHolderBoxDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocHolderBox, fieldKey); } + _prevSelectionDisposer: IReactionDisposer | undefined; + _dropDisposer?: DragManager.DragDropDisposer; + _selections: Doc[] = []; + _contRef = React.createRef(); + _curSelection = -1; + componentDidMount() { + this._prevSelectionDisposer = reaction(() => this.layoutDoc[this.props.fieldKey], (data) => { + if (data instanceof Doc && !this.isSelectionLocked()) { + this._selections.indexOf(data) !== -1 && this._selections.splice(this._selections.indexOf(data), 1); + this._selections.push(data); + this._curSelection = this._selections.length - 1; + } + }); + } + componentWillUnmount() { + this._prevSelectionDisposer?.(); + } + specificContextMenu = (e: React.MouseEvent): void => { + const funcs: ContextMenuProps[] = []; + funcs.push({ description: (this.isSelectionLocked() ? "Show" : "Lock") + " Selection", event: () => this.toggleLockSelection, icon: "expand-arrows-alt" }); + funcs.push({ description: (this.layoutDoc.excludeCollections ? "Include" : "Exclude") + " Collections", event: () => this.layoutDoc.excludeCollections = !this.layoutDoc.excludeCollections, icon: "expand-arrows-alt" }); + funcs.push({ description: `${this.layoutDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.layoutDoc.forceActive = !this.layoutDoc.forceActive, icon: "project-diagram" }); + funcs.push({ description: `Show ${this.layoutDoc.childLayoutTemplateName !== "keyValue" ? "key values" : "contents"}`, event: () => this.layoutDoc.childLayoutString = this.layoutDoc.childLayoutString ? undefined : "", icon: "project-diagram" }); + + ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); + } + lockSelection = () => { + this.layoutDoc[this.props.fieldKey] = this.layoutDoc[this.props.fieldKey]; + } + showSelection = () => { + this.layoutDoc[this.props.fieldKey] = ComputedField.MakeFunction(`selectedDocs(self,this.excludeCollections,[_last_])?.[0]`); + } + isSelectionLocked = () => { + const kvpstring = Field.toKeyValueString(this.layoutDoc, this.props.fieldKey); + return !kvpstring || kvpstring.includes("DOC"); + } + toggleLockSelection = () => { + !this.isSelectionLocked() ? this.lockSelection() : this.showSelection(); + return true; + } + prevSelection = () => { + this.lockSelection(); + if (this._curSelection > 0) { + this.layoutDoc[this.props.fieldKey] = this._selections[--this._curSelection]; + return true; + } + } + nextSelection = () => { + if (this._curSelection < this._selections.length - 1 && this._selections.length) { + this.layoutDoc[this.props.fieldKey] = this._selections[++this._curSelection]; + return true; + } + } + onPointerDown = (e: React.PointerEvent) => { + if (this.active() && e.button === 0 && !e.ctrlKey) { + e.stopPropagation(); + } + } + onLockClick = (e: React.MouseEvent) => { + this.toggleLockSelection(); + (e.nativeEvent as any).formattedHandled = true; + e.stopPropagation(); + } + get xPad() { return NumCast(this.props.Document._xPadding); } + get yPad() { return NumCast(this.props.Document._yPadding); } + onClick = (e: React.MouseEvent) => { + let hitWidget: boolean | undefined = false; + if (this._contRef.current!.getBoundingClientRect().top + this.yPad > e.clientY) hitWidget = (() => { this.props.select(false); return true; })(); + else if (this._contRef.current!.getBoundingClientRect().bottom - this.yPad < e.clientY) hitWidget = (() => { this.props.select(false); return true; })(); + else { + if (this._contRef.current!.getBoundingClientRect().left + this.xPad > e.clientX) hitWidget = this.prevSelection(); + if (this._contRef.current!.getBoundingClientRect().right - this.xPad < e.clientX) hitWidget = this.nextSelection(); + } + if (hitWidget) { + (e.nativeEvent as any).formattedHandled = true; + e.stopPropagation(); + } + } + pwidth = () => this.props.PanelWidth() - 2 * this.xPad; + pheight = () => this.props.PanelHeight() - 2 * this.yPad; + getTransform = () => this.props.ScreenToLocalTransform().translate(-this.xPad, -this.yPad); + isActive = () => this.active() || !this.props.renderDepth; + layoutTemplateDoc = () => Cast(this.props.Document.childLayoutTemplate, Doc, null); + get renderContents() { + const containedDoc = Cast(this.dataDoc[this.props.fieldKey], Doc, null); + const layoutTemplate = StrCast(this.layoutDoc.childLayoutString); + const contents = !(containedDoc instanceof Doc) ? (null) : this.layoutDoc.childLayoutString || this.layoutTemplateDoc() ? + : + ; + return contents; + } + render() { + TraceMobx(); + return
+ {this.renderContents} +
+ +
+
; + } + + @undoBatch + @action + drop = (e: Event, de: DragManager.DropEvent) => { + if (de.complete.docDragData) { + if (de.complete.docDragData.draggedDocuments[0].type === DocumentType.FONTICON) { + const doc = Cast(de.complete.docDragData.draggedDocuments[0].dragFactory, Doc, null); + this.props.Document.childLayoutTemplate = doc; + } + } + } + protected createDropTarget = (ele: HTMLDivElement) => { + this._dropDisposer?.(); + ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document)); + } + +} diff --git a/src/client/views/nodes/DocumentBox.scss b/src/client/views/nodes/DocumentBox.scss deleted file mode 100644 index 3a27c16c1..000000000 --- a/src/client/views/nodes/DocumentBox.scss +++ /dev/null @@ -1,15 +0,0 @@ -.documentBox-container { - width: 100%; - height: 100%; - pointer-events: all; - background: rgb(241, 239, 235); - position: absolute; - .documentBox-lock { - margin: auto; - color: white; - position: absolute; - } - .contentFittingDocumentView { - position: absolute; - } -} \ No newline at end of file diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx deleted file mode 100644 index b53c7cfe6..000000000 --- a/src/client/views/nodes/DocumentBox.tsx +++ /dev/null @@ -1,207 +0,0 @@ -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, IReactionDisposer, reaction } from "mobx"; -import { observer } from "mobx-react"; -import { Doc, Field } from "../../../new_fields/Doc"; -import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas"; -import { makeInterface } from "../../../new_fields/Schema"; -import { ComputedField } from "../../../new_fields/ScriptField"; -import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { TraceMobx } from "../../../new_fields/util"; -import { emptyPath, returnFalse, returnOne, returnZero } from "../../../Utils"; -import { DocumentType } from "../../documents/DocumentTypes"; -import { DragManager } from "../../util/DragManager"; -import { undoBatch } from "../../util/UndoManager"; -import { ContextMenu } from "../ContextMenu"; -import { ContextMenuProps } from "../ContextMenuItem"; -import { ViewBoxAnnotatableComponent } from "../DocComponent"; -import { ContentFittingDocumentView } from "./ContentFittingDocumentView"; -import "./DocumentBox.scss"; -import { DocumentView } from "./DocumentView"; -import { FieldView, FieldViewProps } from "./FieldView"; -import React = require("react"); - -type DocHolderBoxSchema = makeInterface<[typeof documentSchema, typeof collectionSchema]>; -const DocHolderBoxDocument = makeInterface(documentSchema, collectionSchema); - -@observer -export class DocHolderBox extends ViewBoxAnnotatableComponent(DocHolderBoxDocument) { - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocHolderBox, fieldKey); } - _prevSelectionDisposer: IReactionDisposer | undefined; - _dropDisposer?: DragManager.DragDropDisposer; - _selections: Doc[] = []; - _contRef = React.createRef(); - _curSelection = -1; - componentDidMount() { - this._prevSelectionDisposer = reaction(() => this.layoutDoc[this.props.fieldKey], (data) => { - if (data instanceof Doc && !this.isSelectionLocked()) { - this._selections.indexOf(data) !== -1 && this._selections.splice(this._selections.indexOf(data), 1); - this._selections.push(data); - this._curSelection = this._selections.length - 1; - } - }); - } - componentWillUnmount() { - this._prevSelectionDisposer?.(); - } - specificContextMenu = (e: React.MouseEvent): void => { - const funcs: ContextMenuProps[] = []; - funcs.push({ description: (this.isSelectionLocked() ? "Show" : "Lock") + " Selection", event: () => this.toggleLockSelection, icon: "expand-arrows-alt" }); - funcs.push({ description: (this.layoutDoc.excludeCollections ? "Include" : "Exclude") + " Collections", event: () => this.layoutDoc.excludeCollections = !this.layoutDoc.excludeCollections, icon: "expand-arrows-alt" }); - funcs.push({ description: `${this.layoutDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.layoutDoc.forceActive = !this.layoutDoc.forceActive, icon: "project-diagram" }); - funcs.push({ description: `Show ${this.layoutDoc.childLayoutTemplateName !== "keyValue" ? "key values" : "contents"}`, event: () => this.layoutDoc.childLayoutString = this.layoutDoc.childLayoutString ? undefined : "", icon: "project-diagram" }); - - ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); - } - lockSelection = () => { - this.layoutDoc[this.props.fieldKey] = this.layoutDoc[this.props.fieldKey]; - } - showSelection = () => { - this.layoutDoc[this.props.fieldKey] = ComputedField.MakeFunction(`selectedDocs(self,this.excludeCollections,[_last_])?.[0]`); - } - isSelectionLocked = () => { - const kvpstring = Field.toKeyValueString(this.layoutDoc, this.props.fieldKey); - return !kvpstring || kvpstring.includes("DOC"); - } - toggleLockSelection = () => { - !this.isSelectionLocked() ? this.lockSelection() : this.showSelection(); - return true; - } - prevSelection = () => { - this.lockSelection(); - if (this._curSelection > 0) { - this.layoutDoc[this.props.fieldKey] = this._selections[--this._curSelection]; - return true; - } - } - nextSelection = () => { - if (this._curSelection < this._selections.length - 1 && this._selections.length) { - this.layoutDoc[this.props.fieldKey] = this._selections[++this._curSelection]; - return true; - } - } - onPointerDown = (e: React.PointerEvent) => { - if (this.active() && e.button === 0 && !e.ctrlKey) { - e.stopPropagation(); - } - } - onLockClick = (e: React.MouseEvent) => { - this.toggleLockSelection(); - (e.nativeEvent as any).formattedHandled = true; - e.stopPropagation(); - } - get xPad() { return NumCast(this.props.Document._xPadding); } - get yPad() { return NumCast(this.props.Document._yPadding); } - onClick = (e: React.MouseEvent) => { - let hitWidget: boolean | undefined = false; - if (this._contRef.current!.getBoundingClientRect().top + this.yPad > e.clientY) hitWidget = (() => { this.props.select(false); return true; })(); - else if (this._contRef.current!.getBoundingClientRect().bottom - this.yPad < e.clientY) hitWidget = (() => { this.props.select(false); return true; })(); - else { - if (this._contRef.current!.getBoundingClientRect().left + this.xPad > e.clientX) hitWidget = this.prevSelection(); - if (this._contRef.current!.getBoundingClientRect().right - this.xPad < e.clientX) hitWidget = this.nextSelection(); - } - if (hitWidget) { - (e.nativeEvent as any).formattedHandled = true; - e.stopPropagation(); - } - } - pwidth = () => this.props.PanelWidth() - 2 * this.xPad; - pheight = () => this.props.PanelHeight() - 2 * this.yPad; - getTransform = () => this.props.ScreenToLocalTransform().translate(-this.xPad, -this.yPad); - isActive = () => this.active() || !this.props.renderDepth; - layoutTemplateDoc = () => Cast(this.props.Document.childLayoutTemplate, Doc, null); - get renderContents() { - const containedDoc = Cast(this.dataDoc[this.props.fieldKey], Doc, null); - const layoutTemplate = StrCast(this.layoutDoc.childLayoutString); - const contents = !(containedDoc instanceof Doc) ? (null) : this.layoutDoc.childLayoutString || this.layoutTemplateDoc() ? - : - ; - return contents; - } - render() { - TraceMobx(); - return
- {this.renderContents} -
- -
-
; - } - - @undoBatch - @action - drop = (e: Event, de: DragManager.DropEvent) => { - if (de.complete.docDragData) { - if (de.complete.docDragData.draggedDocuments[0].type === DocumentType.FONTICON) { - const doc = Cast(de.complete.docDragData.draggedDocuments[0].dragFactory, Doc, null); - this.props.Document.childLayoutTemplate = doc; - } - } - } - protected createDropTarget = (ele: HTMLDivElement) => { - this._dropDisposer?.(); - ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document)); - } - -} diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 81667e549..395a6e0b2 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -14,7 +14,7 @@ import { LabelBox } from "./LabelBox"; import { SliderBox } from "./SliderBox"; import { LinkBox } from "./LinkBox"; import { ScriptingBox } from "./ScriptingBox"; -import { DocHolderBox } from "./DocumentBox"; +import { DocHolderBox } from "./DocHolderBox"; import { DocumentViewProps } from "./DocumentView"; import "./DocumentView.scss"; import { FontIconBox } from "./FontIconBox"; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7a81801e1..86e58ca7a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -23,6 +23,7 @@ import { Docs, DocUtils } from "../../documents/Documents"; import { DocumentType } from '../../documents/DocumentTypes'; import { ClientUtils } from '../../util/ClientUtils'; import { DocumentManager } from "../../util/DocumentManager"; +import { SnappingManager } from '../../util/SnappingManager'; import { DragManager, dropActionType } from "../../util/DragManager"; import { InteractionUtils } from '../../util/InteractionUtils'; import { Scripting } from '../../util/Scripting'; @@ -1105,7 +1106,7 @@ export class DocumentView extends DocComponent(Docu } @computed get ignorePointerEvents() { return this.props.pointerEvents === false || - (this.Document.isBackground && !this.isSelected() && !DragManager.Vals.Instance.GetIsDragging()) || + (this.Document.isBackground && !this.isSelected() && !SnappingManager.GetIsDragging()) || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None); } @undoBatch diff --git a/src/client/views/nodes/QueryBox.tsx b/src/client/views/nodes/QueryBox.tsx index d248b098c..2a21a380d 100644 --- a/src/client/views/nodes/QueryBox.tsx +++ b/src/client/views/nodes/QueryBox.tsx @@ -5,13 +5,12 @@ import { documentSchema } from "../../../new_fields/documentSchemas"; import { Id } from '../../../new_fields/FieldSymbols'; import { makeInterface, listSpec } from "../../../new_fields/Schema"; import { StrCast, Cast } from "../../../new_fields/Types"; -import { SelectionManager } from "../../util/SelectionManager"; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { SearchBox } from "../search/SearchBox"; import { FieldView, FieldViewProps } from './FieldView'; import "./QueryBox.scss"; import { List } from "../../../new_fields/List"; -import { DragManager } from "../../util/DragManager"; +import { SnappingManager } from "../../util/SnappingManager"; type QueryDocument = makeInterface<[typeof documentSchema]>; const QueryDocument = makeInterface(documentSchema); @@ -28,7 +27,7 @@ export class QueryBox extends ViewBoxAnnotatableComponent e.stopPropagation()} > Date: Tue, 5 May 2020 01:36:00 -0400 Subject: fixes for parameterized templates. added a pendingMap to fix issues with expandTemplateLayout. added a switchbox template to test templates. changed dimUnits/dimMagnitude to _dimUnits/_dimMagnitude --- src/Utils.ts | 8 ++++++-- src/client/documents/Documents.ts | 9 ++++----- .../CollectionMulticolumnView.tsx | 22 +++++++++++----------- .../CollectionMultirowView.tsx | 22 +++++++++++----------- .../collectionMulticolumn/MulticolumnResizer.tsx | 14 +++++++------- .../collectionMulticolumn/MultirowResizer.tsx | 14 +++++++------- src/client/views/nodes/DocumentContentsView.tsx | 2 +- .../views/nodes/formattedText/DashFieldView.tsx | 14 +++++++++----- .../views/nodes/formattedText/FormattedTextBox.tsx | 2 +- src/client/views/nodes/formattedText/nodes_rts.ts | 3 ++- src/new_fields/Doc.ts | 9 +++++++-- .../authentication/models/current_user_utils.ts | 19 ++++++++++++++++--- 12 files changed, 82 insertions(+), 56 deletions(-) (limited to 'src/client/documents') diff --git a/src/Utils.ts b/src/Utils.ts index 23b59ac9d..732f74bfc 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -313,14 +313,18 @@ export namespace Utils { } } -export function OmitKeys(obj: any, keys: string[], addKeyFunc?: (dup: any) => void): { omit: any, extract: any } { +export function OmitKeys(obj: any, keys: string[], pattern?: string, addKeyFunc?: (dup: any) => void): { omit: any, extract: any } { const omit: any = { ...obj }; const extract: any = {}; keys.forEach(key => { extract[key] = omit[key]; delete omit[key]; }); - addKeyFunc && addKeyFunc(omit); + pattern && Array.from(Object.keys(omit)).filter(key => key.match(pattern)).forEach(key => { + extract[key] = omit[key]; + delete omit[key]; + }) + addKeyFunc?.(omit); return { omit, extract }; } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f5d6cd7f6..a87a77e1d 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -58,6 +58,8 @@ export interface DocumentOptions { _height?: number; _nativeWidth?: number; _nativeHeight?: number; + _dimMagnitude?: number; // magnitude of collectionMulti{row,col} view element + _dimUnit?: string; // "px" or "*" (default = "*") _fitWidth?: boolean; _fitToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents _LODdisable?: boolean; @@ -455,10 +457,7 @@ export namespace Docs { Scripting.addGlobal(Buxton); - const delegateKeys = ["x", "y", "layoutKey", "_width", "_height", "_panX", "_panY", "_viewType", "_nativeWidth", "_nativeHeight", "dropAction", "childDropAction", "_annotationOn", - "_chromeStatus", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex", "_showSidebar", "_showTitle", "_showCaption", "_showTitleHover", "_backgroundColor", - "_xMargin", "_yMargin", "_xPadding", "_yPadding", "_singleLine", "_scrollTop", - "_color", "isLinkButton", "isBackground", "removeDropProperties", "treeViewOpen"]; + const delegateKeys = ["x", "y", "layoutKey", "dropAction", "childDropAction", "isLinkButton", "isBackground", "removeDropProperties", "treeViewOpen"]; /** * This function receives the relevant document prototype and uses @@ -479,7 +478,7 @@ export namespace Docs { * main document. */ export function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data") { - const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys); + const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys, "^_"); if (!("author" in protoProps)) { protoProps.author = Doc.CurrentUserEmail; diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 38b16e184..a09124304 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -46,12 +46,12 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu */ @computed private get ratioDefinedDocs() { - return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout.dimUnit, "*") === DimUnit.Ratio); + return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, "*") === DimUnit.Ratio); } /** - * This loops through all childLayoutPairs and extracts the values for dimUnit - * and dimMagnitude, ignoring any that are malformed. Additionally, it then + * This loops through all childLayoutPairs and extracts the values for _dimUnit + * and _dimMagnitude, ignoring any that are malformed. Additionally, it then * normalizes the ratio values so that one * value is always 1, with the remaining * values proportionate to that easily readable metric. * @returns the list of the resolved width specifiers (unit and magnitude pairs) @@ -62,8 +62,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu let starSum = 0; const widthSpecifiers: WidthSpecifier[] = []; this.childLayoutPairs.map(pair => { - const unit = StrCast(pair.layout.dimUnit, "*"); - const magnitude = NumCast(pair.layout.dimMagnitude, 1); + const unit = StrCast(pair.layout._dimUnit, "*"); + const magnitude = NumCast(pair.layout._dimMagnitude, 1); if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) { (unit === DimUnit.Ratio) && (starSum += magnitude); widthSpecifiers.push({ magnitude, unit }); @@ -83,9 +83,9 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu setTimeout(() => { const { ratioDefinedDocs } = this; if (this.childLayoutPairs.length) { - const minimum = Math.min(...ratioDefinedDocs.map(doc => NumCast(doc.dimMagnitude, 1))); + const minimum = Math.min(...ratioDefinedDocs.map(doc => NumCast(doc._dimMagnitude, 1))); if (minimum !== 0) { - ratioDefinedDocs.forEach(layout => layout.dimMagnitude = NumCast(layout.dimMagnitude, 1) / minimum, 1); + ratioDefinedDocs.forEach(layout => layout._dimMagnitude = NumCast(layout._dimMagnitude, 1) / minimum, 1); } } }); @@ -161,8 +161,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu if (columnUnitLength === undefined) { return 0; // we're still waiting on promises to resolve } - let width = NumCast(layout.dimMagnitude, 1); - if (StrCast(layout.dimUnit, "*") === DimUnit.Ratio) { + let width = NumCast(layout._dimMagnitude, 1); + if (StrCast(layout._dimUnit, "*") === DimUnit.Ratio) { width *= columnUnitLength; } return width; @@ -194,8 +194,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu onInternalDrop = (e: Event, de: DragManager.DropEvent) => { if (super.onInternalDrop(e, de)) { de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => { - d.dimUnit = "*"; - d.dimMagnitude = 1; + d._dimUnit = "*"; + d._dimMagnitude = 1; })); } return false; diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index b55b67a9e..4326723b1 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -46,12 +46,12 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) */ @computed private get ratioDefinedDocs() { - return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout.dimUnit, "*") === DimUnit.Ratio); + return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, "*") === DimUnit.Ratio); } /** - * This loops through all childLayoutPairs and extracts the values for dimUnit - * and dimUnit, ignoring any that are malformed. Additionally, it then + * This loops through all childLayoutPairs and extracts the values for _dimUnit + * and _dimUnit, ignoring any that are malformed. Additionally, it then * normalizes the ratio values so that one * value is always 1, with the remaining * values proportionate to that easily readable metric. * @returns the list of the resolved width specifiers (unit and magnitude pairs) @@ -62,8 +62,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) let starSum = 0; const heightSpecifiers: HeightSpecifier[] = []; this.childLayoutPairs.map(pair => { - const unit = StrCast(pair.layout.dimUnit, "*"); - const magnitude = NumCast(pair.layout.dimMagnitude, 1); + const unit = StrCast(pair.layout._dimUnit, "*"); + const magnitude = NumCast(pair.layout._dimMagnitude, 1); if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) { (unit === DimUnit.Ratio) && (starSum += magnitude); heightSpecifiers.push({ magnitude, unit }); @@ -83,9 +83,9 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) setTimeout(() => { const { ratioDefinedDocs } = this; if (this.childLayoutPairs.length) { - const minimum = Math.min(...ratioDefinedDocs.map(layout => NumCast(layout.dimMagnitude, 1))); + const minimum = Math.min(...ratioDefinedDocs.map(layout => NumCast(layout._dimMagnitude, 1))); if (minimum !== 0) { - ratioDefinedDocs.forEach(layout => layout.dimMagnitude = NumCast(layout.dimMagnitude, 1) / minimum); + ratioDefinedDocs.forEach(layout => layout._dimMagnitude = NumCast(layout._dimMagnitude, 1) / minimum); } } }); @@ -161,8 +161,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) if (rowUnitLength === undefined) { return 0; // we're still waiting on promises to resolve } - let height = NumCast(layout.dimMagnitude, 1); - if (StrCast(layout.dimUnit, "*") === DimUnit.Ratio) { + let height = NumCast(layout._dimMagnitude, 1); + if (StrCast(layout._dimUnit, "*") === DimUnit.Ratio) { height *= rowUnitLength; } return height; @@ -194,8 +194,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) onInternalDrop = (e: Event, de: DragManager.DropEvent) => { if (super.onInternalDrop(e, de)) { de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => { - d.dimUnit = "*"; - d.dimMagnitude = 1; + d._dimUnit = "*"; + d._dimMagnitude = 1; })); } return false; diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx index e1e604686..a24eb1631 100644 --- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx +++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx @@ -43,12 +43,12 @@ export default class ResizeBar extends React.Component { const unitLength = columnUnitLength(); if (unitLength) { if (toNarrow) { - const scale = StrCast(toNarrow.dimUnit, "*") === DimUnit.Ratio ? unitLength : 1; - toNarrow.dimMagnitude = Math.max(0.05, NumCast(toNarrow.dimMagnitude, 1) - Math.abs(movementX) / scale); + const scale = StrCast(toNarrow._dimUnit, "*") === DimUnit.Ratio ? unitLength : 1; + toNarrow._dimMagnitude = Math.max(0.05, NumCast(toNarrow._dimMagnitude, 1) - Math.abs(movementX) / scale); } if (toWiden) { - const scale = StrCast(toWiden.dimUnit, "*") === DimUnit.Ratio ? unitLength : 1; - toWiden.dimMagnitude = Math.max(0.05, NumCast(toWiden.dimMagnitude, 1) + Math.abs(movementX) / scale); + const scale = StrCast(toWiden._dimUnit, "*") === DimUnit.Ratio ? unitLength : 1; + toWiden._dimMagnitude = Math.max(0.05, NumCast(toWiden._dimMagnitude, 1) + Math.abs(movementX) / scale); } } } @@ -56,17 +56,17 @@ export default class ResizeBar extends React.Component { private get isActivated() { const { toLeft, toRight } = this.props; if (toLeft && toRight) { - if (StrCast(toLeft.dimUnit, "*") === DimUnit.Pixel && StrCast(toRight.dimUnit, "*") === DimUnit.Pixel) { + if (StrCast(toLeft._dimUnit, "*") === DimUnit.Pixel && StrCast(toRight._dimUnit, "*") === DimUnit.Pixel) { return false; } return true; } else if (toLeft) { - if (StrCast(toLeft.dimUnit, "*") === DimUnit.Pixel) { + if (StrCast(toLeft._dimUnit, "*") === DimUnit.Pixel) { return false; } return true; } else if (toRight) { - if (StrCast(toRight.dimUnit, "*") === DimUnit.Pixel) { + if (StrCast(toRight._dimUnit, "*") === DimUnit.Pixel) { return false; } return true; diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx index 9df8cc3e2..5f00b18b9 100644 --- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx +++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx @@ -41,12 +41,12 @@ export default class ResizeBar extends React.Component { const unitLength = columnUnitLength(); if (unitLength) { if (toNarrow) { - const scale = StrCast(toNarrow.dimUnit, "*") === DimUnit.Ratio ? unitLength : 1; - toNarrow.dimMagnitude = Math.max(0.05, NumCast(toNarrow.dimMagnitude, 1) - Math.abs(movementY) / scale); + const scale = StrCast(toNarrow._dimUnit, "*") === DimUnit.Ratio ? unitLength : 1; + toNarrow._dimMagnitude = Math.max(0.05, NumCast(toNarrow._dimMagnitude, 1) - Math.abs(movementY) / scale); } if (toWiden) { - const scale = StrCast(toWiden.dimUnit, "*") === DimUnit.Ratio ? unitLength : 1; - toWiden.dimMagnitude = Math.max(0.05, NumCast(toWiden.dimMagnitude, 1) + Math.abs(movementY) / scale); + const scale = StrCast(toWiden._dimUnit, "*") === DimUnit.Ratio ? unitLength : 1; + toWiden._dimMagnitude = Math.max(0.05, NumCast(toWiden._dimMagnitude, 1) + Math.abs(movementY) / scale); } } } @@ -54,17 +54,17 @@ export default class ResizeBar extends React.Component { private get isActivated() { const { toTop, toBottom } = this.props; if (toTop && toBottom) { - if (StrCast(toTop.dimUnit, "*") === DimUnit.Pixel && StrCast(toBottom.dimUnit, "*") === DimUnit.Pixel) { + if (StrCast(toTop._dimUnit, "*") === DimUnit.Pixel && StrCast(toBottom._dimUnit, "*") === DimUnit.Pixel) { return false; } return true; } else if (toTop) { - if (StrCast(toTop.dimUnit, "*") === DimUnit.Pixel) { + if (StrCast(toTop._dimUnit, "*") === DimUnit.Pixel) { return false; } return true; } else if (toBottom) { - if (StrCast(toBottom.dimUnit, "*") === DimUnit.Pixel) { + if (StrCast(toBottom._dimUnit, "*") === DimUnit.Pixel) { return false; } return true; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 395a6e0b2..2fc82e524 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -135,7 +135,7 @@ export class DocumentContentsView extends React.Component, onInput: Opt): JsxBindings { const list = { - ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit, + ...OmitKeys(this.props, ['parentActive'], "", (obj: any) => obj.active = this.props.parentActive).omit, RootDoc: Cast(this.layoutDoc?.rootDocument, Doc, null) || this.layoutDoc, Document: this.layoutDoc, DataDoc: this.dataDoc, diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index d87d6e424..f28057fd7 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -34,6 +34,7 @@ export class DashFieldView { docid={node.attrs.docid} width={node.attrs.width} height={node.attrs.height} + hideKey={node.attrs.hideKey} tbox={tbox} />, this._fieldWrapper); (this as any).dom = this._fieldWrapper; @@ -47,6 +48,7 @@ export class DashFieldView { interface IDashFieldViewInternal { fieldKey: string; docid: string; + hideKey: boolean; tbox: FormattedTextBox; width: number; height: number; @@ -80,7 +82,7 @@ export class DashFieldViewInternal extends React.Component - - {this._fieldKey} - + {this.props.hideKey ? (null) : + + {this._fieldKey} + }
{this.fieldValueContent} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 658a55f51..f5c28b350 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -205,7 +205,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if (!this._applyingChange && json.replace(/"selection":.*/, "") !== curProto?.Data.replace(/"selection":.*/, "")) { this._applyingChange = true; this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); - if ((!curTemp && !curProto) || curText) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended) + if ((!curTemp && !curProto) || curText || curLayout?.Data.includes("dash")) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended) if (curText !== curLayout?.Text) { this.dataDoc[this.props.fieldKey] = new RichTextField(json, curText); this.dataDoc[this.props.fieldKey + "-noTemplate"] = (curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts index e7bcf444a..af39ef9c7 100644 --- a/src/client/views/nodes/formattedText/nodes_rts.ts +++ b/src/client/views/nodes/formattedText/nodes_rts.ts @@ -166,7 +166,8 @@ export const nodes: { [index: string]: NodeSpec } = { inline: true, attrs: { fieldKey: { default: "" }, - docid: { default: "" } + docid: { default: "" }, + hideKey: { default: false } }, group: "inline", draggable: false, diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 9f75b5ffe..b41007e9c 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -467,6 +467,7 @@ export namespace Doc { return (layoutDoc.isTemplateForField || layoutDoc.isTemplateDoc) && dataDoc && layoutDoc !== dataDoc; } + const _pendingMap: Map = new Map(); // // Returns an expanded template layout for a target data document if there is a template relationship // between the two. If so, the layoutDoc is expanded into a new document that inherits the properties @@ -492,13 +493,16 @@ export namespace Doc { const layoutFielddKey = Doc.LayoutFieldKey(templateLayoutDoc); const expandedLayoutFieldKey = (templateField || layoutFielddKey) + "-layout[" + templateLayoutDoc[Id] + (args ? `(${args})` : "") + "]"; let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey]; + if (templateLayoutDoc.resolvedDataDoc instanceof Promise) { expandedTemplateLayout = undefined; + _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey, true); } - else if (expandedTemplateLayout === undefined) { - if (templateLayoutDoc.resolvedDataDoc === Doc.GetProto(targetDoc) && templateLayoutDoc.PARAMS === StrCast(targetDoc.PARAMS)) { + else if (expandedTemplateLayout === undefined && !_pendingMap.get(targetDoc[Id] + expandedLayoutFieldKey + args)) { + if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc)) && templateLayoutDoc.PARAMS === StrCast(targetDoc.PARAMS)) { expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params } else { + _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey + args, true); templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = Cast(templateLayoutDoc.proto, Doc, null) || templateLayoutDoc); // if the template has already been applied (ie, a nested template), then use the template's prototype setTimeout(action(() => { if (!targetDoc[expandedLayoutFieldKey]) { @@ -513,6 +517,7 @@ export namespace Doc { if (dataDoc[templateField] === undefined && templateLayoutDoc[templateField] instanceof List) { dataDoc[templateField] = ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"] as List)`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc }); } + _pendingMap.delete(targetDoc[Id] + expandedLayoutFieldKey + args); } }), 0); } diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index b10c6f119..d3e085b21 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -22,6 +22,7 @@ import { MainView } from "../../../client/views/MainView"; import { DocumentType } from "../../../client/documents/DocumentTypes"; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import { faBoxOpen } from "@fortawesome/free-solid-svg-icons"; +import { CollectionMulticolumnView, DimUnit } from "../../../client/views/collections/collectionMulticolumn/CollectionMulticolumnView"; export class CurrentUserUtils { private static curr_id: string; @@ -87,16 +88,28 @@ export class CurrentUserUtils { } if (doc["template-button-switch"] === undefined) { - const { FreeformDocument, MulticolumnDocument } = Docs.Create; + const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create; - const yes = FreeformDocument([], { title: "yes", _height: 100, _width: 100, _LODdisable: true }); + const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _LODdisable: true, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40 }); + const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1 }); const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, _LODdisable: true }); + const labelTemplate = { + doc: { + type: "doc", content: [{ + type: "paragraph", + content: [{ type: "dashField", attrs: { fieldKey: "PARAMS", hideKey: true } }] + }] + }, + selection: { type: "text", anchor: 1, head: 1 }, + storedMarks: [] + }; + Doc.GetProto(name).text = new RichTextField(JSON.stringify(labelTemplate), "PARAMS"); Doc.GetProto(yes).backgroundColor = ComputedField.MakeFunction("self[this.PARAMS] ? 'green':'red'"); // Doc.GetProto(no).backgroundColor = ComputedField.MakeFunction("!self[this.PARAMS] ? 'red':'white'"); // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = true"); Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = !self[this.PARAMS]"); // Doc.GetProto(no).onClick = ScriptField.MakeScript("self[this.PARAMS] = false"); - const box = MulticolumnDocument([/*no, */ yes], { title: "value", _width: 40, _height: 40, }); + const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, }); box.isTemplateDoc = makeTemplate(box, true, "switch"); doc["template-button-switch"] = CurrentUserUtils.ficon({ -- cgit v1.2.3-70-g09d2 From f1473fc5bf7f2b3109be358ae14d28725240284c Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 11 May 2020 22:30:33 -0400 Subject: cleaned up menu items. improved snapping with fixed aspect items, but not perfect. --- src/client/documents/Documents.ts | 1 + src/client/util/DragManager.ts | 14 ++-- src/client/views/DocumentDecorations.tsx | 41 ++++++++++-- src/client/views/MainView.tsx | 4 +- .../views/collections/CollectionDockingView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 1 - .../collectionFreeForm/CollectionFreeFormView.tsx | 21 +++--- src/client/views/nodes/DocumentView.tsx | 75 ++++++++-------------- src/client/views/nodes/ImageBox.tsx | 4 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 37 ++++------- 10 files changed, 101 insertions(+), 99 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a87a77e1d..95b8daafc 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -150,6 +150,7 @@ export interface DocumentOptions { 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 clipboard?: Doc; + UseCors?: boolean; icon?: string; sourcePanel?: 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 diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 4f547e2f7..1ee4c57a2 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -256,7 +256,8 @@ export namespace DragManager { SnappingManager.setSnapLines(horizLines, vertLines); } - export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) { + // snap to the active snap lines - if oneAxis is set (eg, for maintaining aspect ratios), then it only snaps to the nearest horizontal/vertical line + export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number, oneAxis: boolean = false) { const snapThreshold = NumCast(Doc.UserDoc()["constants-snapThreshold"], 10); const snapVal = (pts: number[], drag: number, snapLines: number[]) => { if (snapLines.length) { @@ -265,14 +266,15 @@ export namespace DragManager { const closestPts = rangePts.map(pt => snapLines.reduce((nearest, curr) => Math.abs(nearest - pt) > Math.abs(curr - pt) ? curr : nearest)); const closestDists = rangePts.map((pt, i) => Math.abs(pt - closestPts[i])); const minIndex = closestDists[0] < closestDists[1] && closestDists[0] < closestDists[2] ? 0 : closestDists[1] < closestDists[2] ? 1 : 2; - return closestDists[minIndex] < snapThreshold ? closestPts[minIndex] + offs[minIndex] : drag; + return closestDists[minIndex] < snapThreshold ? [closestDists[minIndex], closestPts[minIndex] + offs[minIndex]] : [Number.MAX_VALUE, drag]; } - return drag; + return [Number.MAX_VALUE, drag]; }; - + const xsnap = snapVal([xFromLeft, xFromRight], e.pageX, SnappingManager.vertSnapLines()); + const ysnap = snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.horizSnapLines()); return { - thisX: snapVal([xFromLeft, xFromRight], e.pageX, SnappingManager.vertSnapLines()), - thisY: snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.horizSnapLines()) + thisX: !oneAxis || xsnap[0] < ysnap[0] ? xsnap[1] : e.pageX, + thisY: !oneAxis || xsnap[0] > ysnap[0] ? ysnap[1] : e.pageY }; } export let docsBeingDragged: Doc[] = []; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index a3c476125..b939d96b6 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -20,7 +20,6 @@ import React = require("react"); import { Id } from '../../new_fields/FieldSymbols'; import e = require('express'); import { CollectionDockingView } from './collections/CollectionDockingView'; -import { MainView } from './MainView'; import { SnappingManager } from '../util/SnappingManager'; library.add(faCaretUp); @@ -238,6 +237,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } _initialAutoHeight = false; + _dragHeights = new Map(); @action onPointerDown = (e: React.PointerEvent): void => { setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, (e) => { }); @@ -253,17 +253,38 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> this._snapX = e.pageX; this._snapY = e.pageY; this._initialAutoHeight = true; + DragManager.docsBeingDragged = SelectionManager.SelectedDocuments().map(dv => dv.rootDoc); + SelectionManager.SelectedDocuments().map(dv => { + this._dragHeights.set(dv.layoutDoc, NumCast(dv.layoutDoc._height)); + dv.layoutDoc._delayAutoHeight = dv.layoutDoc._height; + }); } onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => { - const { thisX, thisY } = DragManager.snapDrag(e, -this._offX, -this._offY, this._offX, this._offY); + const first = SelectionManager.SelectedDocuments()[0]; + const fixedAspect = NumCast(first.layoutDoc._nativeWidth) !== 0; + let { thisX, thisY } = DragManager.snapDrag(e, -this._offX, -this._offY, this._offX, this._offY, fixedAspect); + if (fixedAspect) { // if aspect is set, then any snapped movement must be coerced to match the aspect ratio + const aspect = NumCast(first.layoutDoc._nativeWidth) / NumCast(first.layoutDoc._nativeHeight); + const deltaX = thisX - this._snapX; + const deltaY = thisY - this._snapY; + if (thisX !== e.pageX) { + const snapY = deltaX / aspect + this._snapY; + thisY = Math.abs(deltaX / aspect) < 10 ? snapY : thisY; + } else { + const snapX = deltaY * aspect + this._snapX; + thisX = Math.abs(deltaY * aspect) < 10 ? snapX : thisX; + } + } move[0] = thisX - this._snapX; move[1] = thisY - this._snapY; this._snapX = thisX; this._snapY = thisY; let dX = 0, dY = 0, dW = 0, dH = 0; - + const unfreeze = () => + SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => + (element.rootDoc.type === DocumentType.RTF && element.layoutDoc._nativeHeight) && element.toggleNativeDimensions())); switch (this._resizeHdlId) { case "": break; case "documentDecorations-topLeftResizer": @@ -278,6 +299,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> dH = -move[1]; break; case "documentDecorations-topResizer": + unfreeze(); dY = -1; dH = -move[1]; break; @@ -291,13 +313,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> dH = move[1]; break; case "documentDecorations-bottomResizer": + unfreeze(); dH = move[1]; break; case "documentDecorations-leftResizer": + unfreeze(); dX = -1; dW = -move[0]; break; case "documentDecorations-rightResizer": + unfreeze(); dW = move[0]; break; } @@ -309,9 +334,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let nwidth = doc._nativeWidth || 0; let nheight = doc._nativeHeight || 0; const width = (doc._width || 0); - const height = (doc._height || (nheight / nwidth * width)); + let height = (doc._height || (nheight / nwidth * width)); const scale = element.props.ScreenToLocalTransform().Scale * element.props.ContentScaling(); if (nwidth && nheight) { + if (nwidth / nheight !== width / height) { + height = nheight / nwidth * width; + } if (Math.abs(dW) > Math.abs(dH)) dH = dW * nheight / nwidth; else dW = dH * nwidth / nheight; } @@ -365,7 +393,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> @action onPointerUp = (e: PointerEvent): void => { SelectionManager.SelectedDocuments().map(dv => { - dv.layoutDoc._delayAutoHeight && (dv.layoutDoc._autoHeight = true); + if (NumCast(dv.layoutDoc._delayAutoHeight) < this._dragHeights.get(dv.layoutDoc)!) { + dv.nativeWidth > 0 && Doc.toggleNativeDimensions(dv.layoutDoc, dv.props.ContentScaling(), dv.panelWidth(), dv.panelHeight()); + dv.layoutDoc._autoHeight = true; + } dv.layoutDoc._delayAutoHeight = undefined; }); this._resizeHdlId = ""; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 62496b01f..9bfef06b4 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -83,14 +83,14 @@ export class MainView extends React.Component { firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag); window.removeEventListener("keydown", KeyManager.Instance.handle); window.addEventListener("keydown", KeyManager.Instance.handle); - window.addEventListener("paste", KeyManager.Instance.paste); + window.addEventListener("paste", KeyManager.Instance.paste as any); } componentWillUnMount() { window.removeEventListener("keydown", KeyManager.Instance.handle); window.removeEventListener("pointerdown", this.globalPointerDown); window.removeEventListener("pointerup", this.globalPointerUp); - window.removeEventListener("paste", KeyManager.Instance.paste); + window.removeEventListener("paste", KeyManager.Instance.paste as any); } constructor(props: Readonly<{}>) { diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 50e3b73eb..2e24cbb12 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -757,7 +757,7 @@ export class DockedFrameRenderer extends React.Component { get layoutDoc() { return this._document && Doc.Layout(this._document); } nativeAspect = () => this.nativeWidth() ? this.nativeWidth() / this.nativeHeight() : 0; panelWidth = () => this.layoutDoc?.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) : - (this.nativeAspect() && this.nativeAspect() < this._panelWidth / this._panelHeight ? this._panelHeight * this.nativeAspect() : this._panelWidth); + (this.nativeAspect() && this.nativeAspect() < this._panelWidth / this._panelHeight ? this._panelHeight * this.nativeAspect() : this._panelWidth) panelHeight = () => this.nativeAspect() && this.nativeAspect() > this._panelWidth / this._panelHeight ? this._panelWidth / this.nativeAspect() : this._panelHeight; nativeWidth = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeWidth) || this._panelWidth : 0; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index ac9f64d94..0f239d385 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -234,7 +234,6 @@ export class CollectionView extends Touchable { if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - this.setupViewTypes("Change Perspective...", (vtype => { this.props.Document._viewType = vtype; return this.props.Document; }), true); this.setupViewTypes("Add a Perspective...", vtype => { const newRendition = Doc.MakeAlias(this.props.Document); newRendition._viewType = vtype; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 129b245b8..6caee960d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1133,19 +1133,25 @@ export class CollectionFreeFormView extends CollectionSubView { - if (this.props.children && this.props.annotationsKey) return; + if (this.props.annotationsKey) return; + + ContextMenu.Instance.addItem({ + description: (this._timelineVisible ? "Close" : "Open") + " Animation Timeline", event: action(() => { + this._timelineVisible = !this._timelineVisible; + }), icon: this._timelineVisible ? faEyeSlash : faEye + }); + const options = ContextMenu.Instance.findByDescription("Options..."); const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : []; optionItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" }); - optionItems.push({ description: !this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze", event: this.toggleNativeDimensions, icon: "snowflake" }); - optionItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" }); + optionItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" }); + optionItems.push({ description: (!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" }); optionItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); optionItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" }); this.props.ContainingCollectionView && optionItems.push({ description: "Promote Collection", event: this.promoteCollection, icon: "table" }); optionItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }); // layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" }); - optionItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document._jitterRotation = (this.props.Document._jitterRotation ? 0 : 10)), icon: "paint-brush" }); optionItems.push({ description: "Import document", icon: "upload", event: ({ x, y }) => { const input = document.createElement("input"); @@ -1173,14 +1179,9 @@ export class CollectionFreeFormView extends CollectionSubView this.Document._LODdisable = !this.Document._LODdisable, icon: "table" }); ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" }); - - ContextMenu.Instance.addItem({ - description: (this._timelineVisible ? "Close" : "Open") + " Animation Timeline", event: action(() => { - this._timelineVisible = !this._timelineVisible; - }), icon: this._timelineVisible ? faEyeSlash : faEye - }); } @observable _timelineVisible = false; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5cccec776..1bf297796 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -704,7 +704,6 @@ export class DocumentView extends DocComponent(Docu } const cm = ContextMenu.Instance; - const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null); const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []); Cast(this.props.Document.contextMenuLabels, listSpec("string"), []).forEach((label, i) => @@ -713,25 +712,16 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" })); - let open = cm.findByDescription("Add a Perspective..."); - const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : []; - openItems.push({ description: "New Echo ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), "onRight"), icon: "layer-group" }); - openItems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" }); - templateDoc && openItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" }); - if (!open) { - open = { description: "Add a Perspective....", subitems: openItems, icon: "external-link-alt" }; - cm.addItem(open); - } - let options = cm.findByDescription("Options..."); const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : []; + const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null); + optionItems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" }); + templateDoc && optionItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" }); if (!options) { options = { description: "Options...", subitems: optionItems, icon: "compass" }; cm.addItem(options); } - cm.moveAfter(options, open); - const existingOnClick = cm.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" }); @@ -764,9 +754,6 @@ export class DocumentView extends DocComponent(Docu moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" }); // cm.addItem({ description: "Copy...", subitems: copies, icon: "copy" }); } - if (Cast(this.props.Document.data, ImageField)) { - moreItems.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" }); - } if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) { moreItems.push({ description: "Export to Google Photos Album", event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: "caret-square-right" }); moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" }); @@ -820,47 +807,38 @@ export class DocumentView extends DocComponent(Docu cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!); runInAction(() => { - if (!ClientUtils.RELEASE) { - const setWriteMode = (mode: DocServer.WriteMode) => { - DocServer.AclsMode = mode; - const mode1 = mode; - const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground; - DocServer.setFieldWriteMode("x", mode1); - DocServer.setFieldWriteMode("y", mode1); - DocServer.setFieldWriteMode("_width", mode1); - DocServer.setFieldWriteMode("_height", mode1); - - DocServer.setFieldWriteMode("_panX", mode2); - DocServer.setFieldWriteMode("_panY", mode2); - DocServer.setFieldWriteMode("scale", mode2); - DocServer.setFieldWriteMode("_viewType", mode2); - }; - const aclsMenu: ContextMenuProps[] = []; - aclsMenu.push({ description: "Default (write/read all)", event: () => setWriteMode(DocServer.WriteMode.Default), icon: DocServer.AclsMode === DocServer.WriteMode.Default ? "check" : "exclamation" }); - aclsMenu.push({ description: "Playground (write own/no read)", event: () => setWriteMode(DocServer.WriteMode.Playground), icon: DocServer.AclsMode === DocServer.WriteMode.Playground ? "check" : "exclamation" }); - aclsMenu.push({ description: "Live Playground (write own/read others)", event: () => setWriteMode(DocServer.WriteMode.LivePlayground), icon: DocServer.AclsMode === DocServer.WriteMode.LivePlayground ? "check" : "exclamation" }); - aclsMenu.push({ description: "Live Readonly (no write/read others)", event: () => setWriteMode(DocServer.WriteMode.LiveReadonly), icon: DocServer.AclsMode === DocServer.WriteMode.LiveReadonly ? "check" : "exclamation" }); - cm.addItem({ description: "Collaboration ACLs...", subitems: aclsMenu, icon: "share" }); - } + const setWriteMode = (mode: DocServer.WriteMode) => { + DocServer.AclsMode = mode; + const mode1 = mode; + const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground; + DocServer.setFieldWriteMode("x", mode1); + DocServer.setFieldWriteMode("y", mode1); + DocServer.setFieldWriteMode("_width", mode1); + DocServer.setFieldWriteMode("_height", mode1); + + DocServer.setFieldWriteMode("_panX", mode2); + DocServer.setFieldWriteMode("_panY", mode2); + DocServer.setFieldWriteMode("scale", mode2); + DocServer.setFieldWriteMode("_viewType", mode2); + }; + const aclsMenu: ContextMenuProps[] = []; + aclsMenu.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "external-link-alt" }); + aclsMenu.push({ description: "Default (write/read all)", event: () => setWriteMode(DocServer.WriteMode.Default), icon: DocServer.AclsMode === DocServer.WriteMode.Default ? "check" : "exclamation" }); + aclsMenu.push({ description: "Playground (write own/no read)", event: () => setWriteMode(DocServer.WriteMode.Playground), icon: DocServer.AclsMode === DocServer.WriteMode.Playground ? "check" : "exclamation" }); + aclsMenu.push({ description: "Live Playground (write own/read others)", event: () => setWriteMode(DocServer.WriteMode.LivePlayground), icon: DocServer.AclsMode === DocServer.WriteMode.LivePlayground ? "check" : "exclamation" }); + aclsMenu.push({ description: "Live Readonly (no write/read others)", event: () => setWriteMode(DocServer.WriteMode.LiveReadonly), icon: DocServer.AclsMode === DocServer.WriteMode.LiveReadonly ? "check" : "exclamation" }); + cm.addItem({ description: "Collaboration ...", subitems: aclsMenu, icon: "share" }); }); runInAction(() => { - cm.addItem({ - description: "Share", - event: () => SharingManager.Instance.open(this), - icon: "external-link-alt" - }); - if (!this.topMost && !(e instanceof Touch)) { // DocumentViews should stop propagation of this event e.stopPropagation(); } ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); - if (!SelectionManager.IsSelected(this, true)) { - SelectionManager.SelectDoc(this, false); - } + !SelectionManager.IsSelected(this, true) && SelectionManager.SelectDoc(this, false); }); const path = this.props.LibraryPath.reduce((p: string, d: Doc) => p + "/" + (Doc.AreProtosEqual(d, (Doc.UserDoc()["tabs-button-library"] as Doc).sourcePanel as Doc) ? "" : d.title), ""); - cm.addItem({ + const item = ({ description: `path: ${path}`, event: () => { if (this.props.LibraryPath !== emptyPath) { this.props.LibraryPath.map(lp => Doc.GetProto(lp).treeViewOpen = lp.treeViewOpen = true); @@ -870,6 +848,7 @@ export class DocumentView extends DocComponent(Docu } }, icon: "check" }); + //cm.addItem(item); } recommender = async () => { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index d85373e98..aaebceaa2 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -29,6 +29,7 @@ import FaceRectangles from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; import React = require("react"); +import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; const requestImageSize = require('../../util/request-image-size'); const path = require('path'); const { Howl } = require('howler'); @@ -158,6 +159,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" }); + funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" }); // funcs.push({ // description: "Reset Native Dimensions", event: action(async () => { // const curNW = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]); @@ -184,7 +187,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent Utils.CopyText(field.url.href), icon: "expand-arrows-alt" }); !existingMore && ContextMenu.Instance.addItem({ description: "More...", subitems: mores, icon: "hand-point-right" }); } } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 3a586ff66..23bf86a32 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -419,9 +419,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const funcs: ContextMenuProps[] = []; this.rootDoc.isTemplateDoc && funcs.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" }); - funcs.push({ description: "Reset Default Layout", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" }); !this.layoutDoc.isTemplateDoc && funcs.push({ - description: "Make Template", event: () => { + description: "Convert to use as a style", event: () => { this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc); Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.rootDoc); }, icon: "eye" @@ -444,11 +443,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }, icon: "eye" }); //funcs.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" }); - funcs.push({ description: !this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze", event: this.toggleNativeDimensions, icon: "snowflake" }); + funcs.push({ description: (!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" }); funcs.push({ description: "Toggle Single Line", event: () => this.layoutDoc._singleLine = !this.layoutDoc._singleLine, icon: "expand-arrows-alt" }); - funcs.push({ description: "Toggle Sidebar", event: () => this.layoutDoc._showSidebar = !this.layoutDoc._showSidebar, icon: "expand-arrows-alt" }); - funcs.push({ description: "Toggle Dictation Icon", event: () => this.layoutDoc._showAudio = !this.layoutDoc._showAudio, icon: "expand-arrows-alt" }); - funcs.push({ description: "Toggle Menubar", event: () => this.toggleMenubar(), icon: "expand-arrows-alt" }); + + const uicontrols: ContextMenuProps[] = []; + uicontrols.push({ description: "Toggle Sidebar", event: () => this.layoutDoc._showSidebar = !this.layoutDoc._showSidebar, icon: "expand-arrows-alt" }); + uicontrols.push({ description: "Toggle Dictation Icon", event: () => this.layoutDoc._showAudio = !this.layoutDoc._showAudio, icon: "expand-arrows-alt" }); + uicontrols.push({ description: "Toggle Menubar", event: () => this.toggleMenubar(), icon: "expand-arrows-alt" }); + + funcs.push({ description: "UI Controls...", subitems: uicontrols, icon: "asterisk" }); const highlighting: ContextMenuProps[] = []; ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option => @@ -481,19 +484,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }); changeItems.push({ description: "FreeForm", event: undoBatch(() => Doc.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), "change view"), icon: "eye" }); !change && cm.addItem({ description: "Change Perspective...", subitems: changeItems, icon: "external-link-alt" }); - - const open = cm.findByDescription("Add a Perspective..."); - const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : []; - - openItems.push({ - description: "FreeForm", event: undoBatch(() => { - const alias = Doc.MakeAlias(this.rootDoc); - Doc.makeCustomViewClicked(alias, Docs.Create.FreeformDocument, "freeform"); - this.props.addDocTab(alias, "onRight"); - }), icon: "eye" - }); - !open && cm.addItem({ description: "Add a Perspective...", subitems: openItems, icon: "external-link-alt" }); - } recordDictation = () => { @@ -663,11 +653,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._disposers.height = reaction( () => this.layoutDoc[HeightSym](), action(height => { - if (height <= 20) { - if (this.layoutDoc._nativeWidth || this.layoutDoc._nativeHeight) { - Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.PanelWidth(), this.props.PanelHeight()); - } - this.layoutDoc._delayAutoHeight = true; + if (height <= 20 && height < NumCast(this.layoutDoc._delayAutoHeight, 20)) { + this.layoutDoc._delayAutoHeight = height; } }) ); @@ -1202,7 +1189,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const dh = NumCast(this.rootDoc._height, 0); const newHeight = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0)); if (Math.abs(newHeight - dh) > 1) { // bcz: Argh! without this, we get into a React crash if the same document is opened in a freeform view and in the treeview. no idea why, but after dragging the freeform document, selecting it, and selecting text, it will compute to 1 pixel higher than the treeview which causes a cycle - if (this.rootDoc !== this.layoutDoc && !this.layoutDoc.resolvedDataDoc) { + if (this.rootDoc !== this.layoutDoc.doc && !this.layoutDoc.resolvedDataDoc) { // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... console.log("Delayed height adjustment..."); setTimeout(() => { -- cgit v1.2.3-70-g09d2 From 54e8450ea88a03bee23cbad3321b5f6bac465e33 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 12 May 2020 13:56:14 -0400 Subject: fixed issues with preselements not updating. fixed focus issues with stacking view for Presentations --- src/client/documents/Documents.ts | 1 + .../views/collections/CollectionStackingView.tsx | 7 ++-- src/client/views/nodes/LabelBox.scss | 1 + src/client/views/nodes/PresBox.tsx | 45 ++++++++++++---------- .../views/presentationview/PresElementBox.tsx | 22 +++++------ 5 files changed, 40 insertions(+), 36 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 95b8daafc..125a531f4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -127,6 +127,7 @@ export interface DocumentOptions { borderRounding?: string; boxShadow?: string; dontRegisterChildViews?: boolean; + lookupField?: ScriptField; // script that returns the value of a field. This script is passed the rootDoc, layoutDoc, field, and container of the document. see PresBox. "onDoubleClick-rawScript"?: string; // onDoubleClick script in raw text form "onClick-rawScript"?: string; // onClick script in raw text form "onCheckedClick-rawScript"?: string; // onChecked script in raw text form diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 97459d910..821a6d476 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -175,8 +175,8 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) focusDocument = (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => boolean) => { - Doc.BrushDoc(this.props.Document); - this.props.focus(this.props.Document); + Doc.BrushDoc(doc); + this.props.focus(doc); Doc.linkFollowHighlight(doc); const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName("documentView-node")).find((node: any) => node.id === doc[Id]); @@ -186,8 +186,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) smoothScroll(500, this._mainCont!, localTop[1] + this._mainCont!.scrollTop); } afterFocus && setTimeout(() => { - if (afterFocus?.()) { - } + if (afterFocus?.()) { } }, 500); } diff --git a/src/client/views/nodes/LabelBox.scss b/src/client/views/nodes/LabelBox.scss index 9e3ff96a7..b605df262 100644 --- a/src/client/views/nodes/LabelBox.scss +++ b/src/client/views/nodes/LabelBox.scss @@ -4,6 +4,7 @@ border-radius: inherit; display: flex; flex-direction: column; + position: absolute; } .labelBox-mainButton { diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 5ff71e922..e203c7efa 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -30,7 +30,7 @@ export class PresBox extends ViewBoxBaseComponent public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); } @observable _isChildActive = false; @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); } - @computed get currentIndex() { return NumCast(this.presElement?.currentIndex); } + @computed get itemIndex() { return NumCast(this.rootDoc._itemIndex); } @computed get presElement() { return Cast(Doc.UserDoc().presElement, Doc, null); } constructor(props: any) { super(props); @@ -42,10 +42,8 @@ export class PresBox extends ViewBoxBaseComponent // this is a design choice -- we could write this data to the presElements which would require a reaction to keep it up to date, and it would prevent // the preselement docs from being part of multiple presentations since they would all have the same field, or we'd have to keep per-presentation data // stored on each pres element. - (this.presElement as Doc).lookupField = ScriptField.MakeScript( - `if (field === 'indexInPres') return docList(container[container.presentationFieldKey]).indexOf(data);` + - `if (field === 'presCollapsedHeight') return container._viewType === '${CollectionViewType.Stacking}' ? 50 : 46;` + - "return undefined;", { field: "string", data: Doc.name, container: Doc.name }); + (this.presElement as Doc).lookupField = ScriptField.MakeFunction("lookupPresBoxField(container, field, data)", + { field: "string", data: Doc.name, container: Doc.name }); } this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox } @@ -61,15 +59,15 @@ export class PresBox extends ViewBoxBaseComponent @action next = () => { this.updateCurrentPresentation(); - if (this.childDocs[this.currentIndex + 1] !== undefined) { - let nextSelected = this.currentIndex + 1; - this.gotoDocument(nextSelected, this.currentIndex); + if (this.childDocs[this.itemIndex + 1] !== undefined) { + let nextSelected = this.itemIndex + 1; + this.gotoDocument(nextSelected, this.itemIndex); for (nextSelected = nextSelected + 1; nextSelected < this.childDocs.length; nextSelected++) { if (!this.childDocs[nextSelected].groupButton) { break; } else { - this.gotoDocument(nextSelected, this.currentIndex); + this.gotoDocument(nextSelected, this.itemIndex); } } } @@ -79,18 +77,18 @@ export class PresBox extends ViewBoxBaseComponent @action back = () => { this.updateCurrentPresentation(); - const docAtCurrent = this.childDocs[this.currentIndex]; + const docAtCurrent = this.childDocs[this.itemIndex]; if (docAtCurrent) { //check if any of the group members had used zooming in including the current document //If so making sure to zoom out, which goes back to state before zooming action - let prevSelected = this.currentIndex; + let prevSelected = this.itemIndex; let didZoom = docAtCurrent.zoomButton; for (; !didZoom && prevSelected > 0 && this.childDocs[prevSelected].groupButton; prevSelected--) { didZoom = this.childDocs[prevSelected].zoomButton; } prevSelected = Math.max(0, prevSelected - 1); - this.gotoDocument(prevSelected, this.currentIndex); + this.gotoDocument(prevSelected, this.itemIndex); } } @@ -194,10 +192,10 @@ export class PresBox extends ViewBoxBaseComponent this.updateCurrentPresentation(); Doc.UnBrushAllDocs(); if (index >= 0 && index < this.childDocs.length) { - this.presElement.currentIndex = index; + this.rootDoc._itemIndex = index; - if (!this.presElement.presStatus) { - this.presElement.presStatus = true; + if (!this.layoutDoc.presStatus) { + this.layoutDoc.presStatus = true; this.startPresentation(index); } @@ -210,12 +208,12 @@ export class PresBox extends ViewBoxBaseComponent //The function that starts or resets presentaton functionally, depending on status flag. startOrResetPres = () => { this.updateCurrentPresentation(); - if (this.presElement.presStatus) { + if (this.layoutDoc.presStatus) { this.resetPresentation(); } else { - this.presElement.presStatus = true; + this.layoutDoc.presStatus = true; this.startPresentation(0); - this.gotoDocument(0, this.currentIndex); + this.gotoDocument(0, this.itemIndex); } } @@ -225,7 +223,7 @@ export class PresBox extends ViewBoxBaseComponent this.updateCurrentPresentation(); this.childDocs.forEach(doc => (doc.presentationTargetDoc as Doc).opacity = 1); this.rootDoc._itemIndex = 0; - this.presElement.presStatus = false; + this.layoutDoc.presStatus = false; } //The function that starts the presentation, also checking if actions should be applied @@ -281,7 +279,7 @@ export class PresBox extends ViewBoxBaseComponent } childLayoutTemplate = () => this.rootDoc._viewType !== CollectionViewType.Stacking ? undefined : this.presElement; removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc); - selectElement = (doc: Doc) => this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.rootDoc._itemIndex)); + selectElement = (doc: Doc) => this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.itemIndex)); getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight panelHeight = () => this.props.PanelHeight() - 20; active = (outsideReaction?: boolean) => ((InkingControl.Instance.selectedTool === InkTool.None && !this.layoutDoc.isBackground) && @@ -329,5 +327,10 @@ export class PresBox extends ViewBoxBaseComponent
; } } -Scripting.addGlobal(function lookupPresBoxField(presLayout: Doc, data: Doc, fieldKey: string) { +Scripting.addGlobal(function lookupPresBoxField(container: Doc, field: string, data: Doc) { + if (field === 'indexInPres') return DocListCast(container[StrCast(container.presentationFieldKey)]).indexOf(data); + if (field === 'presCollapsedHeight') return container._viewType === CollectionViewType.Stacking ? 50 : 46; + if (field === 'presStatus') return container.presStatus; + if (field === '_itemIndex') return container._itemIndex; + return undefined; }); diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index 6d7602268..e13a5f2f7 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -41,8 +41,8 @@ export class PresElementBox extends ViewBoxBaseComponent= this.currentIndex && this.targetDoc) { + if (this.indexInPres >= this.itemIndex && this.targetDoc) { this.targetDoc.opacity = 1; } } else { - if (this.presStatus && this.indexInPres > this.currentIndex && this.targetDoc) { + if (this.presStatus && this.indexInPres > this.itemIndex && this.targetDoc) { this.targetDoc.opacity = 0; } } @@ -82,12 +82,12 @@ export class PresElementBox extends ViewBoxBaseComponent