From da6283875ec7f59af602e14ed19fcab93533f377 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 14 Jun 2022 13:00:27 -0400 Subject: made shared with me doc count decrement when a document is viewed (double-clicked, dragged out) --- src/client/documents/Documents.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/documents/Documents.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b290b2d58..d2937b83f 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -198,7 +198,6 @@ export class DocumentOptions { hideAllLinks?: boolean; // whether all individual blue anchor dots should be hidden isTemplateForField?: string; // the field key for which the containing document is a rendering template isTemplateDoc?: boolean; - watchedDocuments?: Doc; // list of documents an icon doc monitors in order to display a badge count 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) @@ -251,6 +250,7 @@ export class DocumentOptions { numBtnMax?: number; numBtnMin?: number; switchToggle?: boolean; + badgeValue?: ScriptField; //LINEAR VIEW linearViewIsExpanded?: boolean; // is linear view expanded @@ -294,7 +294,7 @@ export class DocumentOptions { treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items. treeViewGrowsHorizontally?: boolean; // whether an embedded tree view of the document can grow horizontally without growing vertically - + treeViewChildDoubleClick?: ScriptField; // // Action Button buttonMenu?: boolean; // whether a action button should be displayed buttonMenuDoc?: Doc; -- cgit v1.2.3-70-g09d2 From 4afe316e6649d915e2bb6f0eecbc9f3cddab7e24 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 22 Jun 2022 23:08:56 -0400 Subject: fixing up Field Infos to work with new initialization. --- src/client/documents/Documents.ts | 124 ++++++++++++++---------------------- src/client/util/CurrentUserUtils.ts | 57 +++++++++++------ 2 files changed, 85 insertions(+), 96 deletions(-) (limited to 'src/client/documents/Documents.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 01cfe90da..ad6a4dd3c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -69,53 +69,57 @@ class EmptyBox { return ""; } } -abstract class FInfo { +export abstract class FInfo { description: string = ""; - type?: string; + fieldType?: string; values?: Field[]; - layoutField?: boolean; // is this field a layout (or datadoc) field // format?: string; // format to display values (e.g, decimal places, $, etc) // parse?: ScriptField; // parse a value from a string - constructor(d: string, l: boolean = false) { this.description = d; this.layoutField = l; } + constructor(d: string) { this.description = d; } } -class BoolInfo extends FInfo { type?= "boolean"; values?: boolean[] = [true, false]; } -class NumInfo extends FInfo { type?= "number"; values?: number[] = []; } -class StrInfo extends FInfo { type?= "string"; values?: string[] = []; } -class DocInfo extends FInfo { type?= "Doc"; values?: Doc[] = []; } -class DimInfo extends FInfo { type?= "DimUnit"; values?= [DimUnit.Pixel, DimUnit.Ratio]; } -class PEInfo extends FInfo { type?= "pointerEvents"; values?= ["all", "none"]; } -class DAInfo extends FInfo { type?= "dropActionType"; values?= ["alias", "copy", "move", "same", "proto", "none"]; } +class BoolInfo extends FInfo { fieldType?= "boolean"; values?: boolean[] = [true, false]; } +class NumInfo extends FInfo { fieldType?= "number"; values?: number[] = []; constructor(d:string, values?:number[]) { super(d); this.values = values; }} +class StrInfo extends FInfo { fieldType?= "string"; values?: string[] = []; constructor(d:string, values?:string[]) { super(d); this.values = values; }} +class DocInfo extends FInfo { fieldType?= "Doc"; values?: Doc[] = []; constructor(d:string, values?:Doc[]) { super(d); this.values = values; }} +class DimInfo extends FInfo { fieldType?= "DimUnit"; values?= [DimUnit.Pixel, DimUnit.Ratio]; } +class PEInfo extends FInfo { fieldType?= "pointerEvents"; values?= ["all", "none"]; } +class DAInfo extends FInfo { fieldType?= "dropActionType"; values?= ["alias", "copy", "move", "same", "proto", "none"]; } type BOOLt = BoolInfo | boolean; -type NUMt = NumInfo | number; -type STRt = StrInfo | string; -type DOCt = DocInfo | Doc; -type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio; -type PEVt = PEInfo | "none" | "all"; +type NUMt = NumInfo | number; +type STRt = StrInfo | string; +type DOCt = DocInfo | Doc; +type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio; +type PEVt = PEInfo | "none" | "all"; type DROPt = DAInfo | dropActionType; export class DocumentOptions { + x?: NUMt = new NumInfo("x coordinate of document in a freeform view"); + y?: NUMt = new NumInfo("y coordinage of document in a freeform view"); + z?: NUMt = new NumInfo("whether document is in overlay (1) or not (0)", [1,0]); system?: BOOLt = new BoolInfo("is this a system created/owned doc"); + type?: STRt = new StrInfo("type of document", Array.from(Object.keys(DocumentType))); + title?: string; _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else"); allowOverlayDrop?: BOOLt = new BoolInfo("can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?"); childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection "); targetDropAction?: DROPt = new DAInfo("what should happen to the source document when ??? "); - userColor?: string; // color associated with a Dash user (seen in header fields of shared documents) - color?: string; // foreground color data doc + userColor?: STRt = new StrInfo("color associated with a Dash user (seen in header fields of shared documents)"); + color?: STRt = new StrInfo("foreground color data doc"); backgroundColor?: STRt = new StrInfo("background color for data doc"); - _backgroundColor?: STRt = new StrInfo("background color for each template layout doc (overrides backgroundColor)", true); - _autoHeight?: BOOLt = new BoolInfo("whether document automatically resizes vertically to display contents", true); - _headerHeight?: NUMt = new NumInfo("height of document header used for displaying title", true); - _headerFontSize?: NUMt = new NumInfo("font size of header of custom notes", true); - _headerPointerEvents?: PEVt = new PEInfo("types of events the header of a custom text document can consume", true); - _panX?: NUMt = new NumInfo("horizontal pan location of a freeform view", true); - _panY?: NUMt = new NumInfo("vertical pan location of a freeform view", true); - _width?: NUMt = new NumInfo("displayed width of a document", true); - _height?: NUMt = new NumInfo("displayed height of document", true); - _nativeWidth?: NUMt = new NumInfo("native width of document contents (e.g., the pixel width of an image)", true); - _nativeHeight?: NUMt = new NumInfo("native height of document contents (e.g., the pixel height of an image)", true); - _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height", true); - _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units", true); - _fitWidth?: BOOLt = new BoolInfo("whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)", true); - _fitContentsToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents + _backgroundColor?: STRt = new StrInfo("background color for each template layout doc (overrides backgroundColor)"); + _autoHeight?: BOOLt = new BoolInfo("whether document automatically resizes vertically to display contents"); + _headerHeight?: NUMt = new NumInfo("height of document header used for displaying title"); + _headerFontSize?: NUMt = new NumInfo("font size of header of custom notes"); + _headerPointerEvents?: PEVt = new PEInfo("types of events the header of a custom text document can consume"); + _panX?: NUMt = new NumInfo("horizontal pan location of a freeform view"); + _panY?: NUMt = new NumInfo("vertical pan location of a freeform view"); + _width?: NUMt = new NumInfo("displayed width of a document"); + _height?: NUMt = new NumInfo("displayed height of document"); + _nativeWidth?: NUMt = new NumInfo("native width of document contents (e.g., the pixel width of an image)"); + _nativeHeight?: NUMt = new NumInfo("native height of document contents (e.g., the pixel height of an image)"); + _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height"); + _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units"); + _fitWidth?: BOOLt = new BoolInfo("whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)"); + _fitContentsToBox?: BOOLt = new BoolInfo("whether a freeformview should zoom/scale to create a shrinkwrapped view of its content"); _contentBounds?: List; // the (forced) bounds of the document to display. format is: [left, top, right, bottom] _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed @@ -155,23 +159,20 @@ export class DocumentOptions { _timecodeToShow?: number; // the time that a document should be displayed (e.g., when an annotation shows up as a video plays) _timecodeToHide?: number; // the time that a document should be hidden _timelineLabel?: boolean; // whether the document exists on a timeline - "_carousel-caption-xMargin"?: number; - "_carousel-caption-yMargin"?: number; - "icon-nativeWidth"?: number; - "icon-nativeHeight"?: number; - "dragFactory-count"?: number; // number of items created from a drag button (used for setting title with incrementing index) - x?: number; - y?: number; - z?: number; // whether document is in overlay (1) or not (0 or undefined) + "_carousel-caption-xMargin"?: NUMt = new NumInfo("x margin of caption inside of a carouself collection"); + "_carousel-caption-yMargin"?: NUMt = new NumInfo("y margin of caption inside of a carouself collection"); + "icon-nativeWidth"?: NUMt = new NumInfo("native width of icon view"); + "icon-nativeHeight"?: NUMt = new NumInfo("native height of icon view"); + "dragFactory-count"?: NUMt = new NumInfo("number of items created from a drag button (used for setting title with incrementing index)"); lat?: number; lng?: number; infoWindowOpen?: boolean; author?: string; _layoutKey?: string; + fieldValues?: List; // possible field values used by fieldInfos + fieldType?: string; // type of afield used by fieldInfos unrendered?: boolean; // denotes an annotation that is not rendered with a DocumentView (e.g, rtf/pdf text selections and links to scroll locations in web/pdf) - type?: string; - title?: string; - "acl-Public"?: string; // public permissions + "acl-Public"?: string; // public permissions "_acl-Public"?: string; // public permissions version?: string; // version identifier for a document label?: string; @@ -340,33 +341,6 @@ export class DocumentOptions { } export namespace Docs { - const _docOptions = new DocumentOptions(); - - export async function setupFieldInfos() { - return await DocServer.GetRefField("FieldInfos8") as Doc ?? - runInAction(() => { - const infos = new Doc("FieldInfos8", true); - const keys = Object.keys(new DocumentOptions()); - for (const key of keys) { - const options = (_docOptions as any)[key] as FInfo; - const finfo = new Doc(); - finfo.name = key; - switch (options.type) { - case "boolean": finfo.options = new List(options.values as any as boolean[]); break; - case "number": finfo.options = new List(options.values as any as number[]); break; - case "Doc": finfo.options = new List(options.values as any as Doc[]); break; - default: // string, pointerEvents, dimUnit, dropActionType - finfo.options = new List(options.values as any as string[]); break; - } - finfo.layoutField = options.layoutField; - finfo.description = options.description; - finfo.type = options.type; - infos[key] = finfo; - } - return infos; - }); - } - export let newAccount: boolean = false; export namespace Prototypes { @@ -798,8 +772,8 @@ export namespace Docs { I.tool = tool; I["text-align"] = "center"; I.title = "ink"; - I.x = options.x; - I.y = options.y; + I.x = options.x as number; + I.y = options.y as number; I._width = options._width as number; I._height = options._height as number; I._fontFamily = "cursive"; @@ -1266,8 +1240,8 @@ export namespace DocUtils { return DocServer.GetRefField(id).then(field => { if (field instanceof Doc) { const alias = Doc.MakeAlias(field); - alias.x = options.x || 0; - alias.y = options.y || 0; + alias.x = options.x as number || 0; + alias.y = options.y as number || 0; alias._width = (options._width as number) || 300; alias._height = (options._height as number) || (options._width as number) || 300; return alias; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index fe9a9b2d9..b9724051a 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -13,7 +13,7 @@ import { ImageField, nullAudio } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; import { OmitKeys, Utils } from "../../Utils"; import { DocServer } from "../DocServer"; -import { Docs, DocumentOptions, DocUtils } from "../documents/Documents"; +import { Docs, DocumentOptions, DocUtils, FInfo } from "../documents/Documents"; import { DocumentType } from "../documents/DocumentTypes"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { CollectionFreeFormView } from "../views/collections/collectionFreeForm"; @@ -39,6 +39,7 @@ import { SnappingManager } from "./SnappingManager"; import { UndoManager } from "./UndoManager"; interface Button { + // DocumentOptions fields a button can set title?: string; toolTip?: string; icon?: string; @@ -51,6 +52,8 @@ interface Button { btnList?: List; ignoreClick?: boolean; buttonText?: string; + + // fields that do not correspond to DocumentOption fields scripts?: { script?: string; onClick?: string; } funcs?: { [key:string]: string }; subMenu?: Button[]; @@ -861,8 +864,7 @@ export class CurrentUserUtils { const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer", _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, buttonText: "Import", icon: "upload", system: true }; - const reqdBtnScripts = { onClick: "importDocument()" }; - return this.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, reqdBtnScripts); + return this.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" }); } static setupClickEditorTemplates(doc: Doc) { @@ -875,9 +877,8 @@ export class CurrentUserUtils { targetScriptKey: "onChildClick", system: true }); - const openDetail = Docs.Create.ScriptingDocument(ScriptField.MakeScript( - "openOnRight(self.doubleClickView)", - {}), { title: "Double click to open doubleClickView", _width: 300, _height: 200, targetScriptKey: "onChildDoubleClick", system: true }); + const openDetail = Docs.Create.ScriptingDocument(ScriptField.MakeScript( "openOnRight(self.doubleClickView)", {}), + { title: "Double click to open doubleClickView", _width: 300, _height: 200, targetScriptKey: "onChildDoubleClick", system: true }); doc["clickFuncs-child"] = Docs.Create.TreeDocument([openInTarget, openDetail], { title: "on Child Click function templates", system: true }); } @@ -914,7 +915,7 @@ export class CurrentUserUtils { } static async updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { - doc.globalGroupDatabase ?? (doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument()); + this.AssignDocField(doc, "globalGroupDatabase", () => Docs.Prototypes.MainGroupDocument(), {}); await DocListCastAsync(DocCast(doc.globalGroupDatabase).data); reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data-lastModified"]), async () => { @@ -954,17 +955,35 @@ export class CurrentUserUtils { this.setupDockedButtons(doc); // the bottom bar of font icons this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left this.setupDocTemplates(doc); // sets up the template menu of templates - doc.globalScriptDatabase ?? ( doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument()); - doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents + this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption + this.AssignDocField(doc, "globalScriptDatabase", (opts) => Docs.Prototypes.MainScriptDocument(), {}); + this.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500); - doc.fieldInfos = await Docs.setupFieldInfos(); if (doc.activeDashboard instanceof Doc) { // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme; } return doc; } + static setupFieldInfos(doc:Doc, field="fieldInfos") { + const fieldInfoOpts = { title: "Field Infos", system: true}; // bcz: all possible document options have associated field infos which are stored onn the FieldInfos document **except for title and system which are used as part of the definition of the fieldInfos object + const infos = this.AssignDocField(doc, field, opts => Doc.assign(new Doc(), opts as any), fieldInfoOpts); + const entries = Object.entries(new DocumentOptions()); + entries.forEach(pair => { + if (!Array.from(Object.keys(fieldInfoOpts)).includes(pair[0])) { + const options = pair[1] as FInfo; + const opts:DocumentOptions = { system: true, title: pair[0], ...OmitKeys(options, ["values"]).omit, fieldIsLayout: pair[0].startsWith("_")}; + switch (options.fieldType) { + case "boolean": opts.fieldValues = new List(options.values as any); break; + case "number": opts.fieldValues = new List(options.values as any); break; + case "Doc": opts.fieldValues = new List(options.values as any); break; + default: opts.fieldValues = new List(options.values as any); break;// string, pointerEvents, dimUnit, dropActionType + } + this.AssignDocField(infos, pair[0], opts => Doc.assign(new Doc(), OmitKeys(opts,["values"]).omit), opts); + } + }); + } public static async loadCurrentUser() { return rp.get(Utils.prepend("/getCurrentUser")).then(async response => { @@ -1007,17 +1026,14 @@ export class CurrentUserUtils { public static _urlState: HistoryUtil.DocUrl; public static openDashboard = (doc: Doc, fromHistory = false) => { - const userDoc = Doc.UserDoc(); CurrentUserUtils.MainDocId = doc[Id]; - if (!DocListCast(CurrentUserUtils.MyDashboards.data).includes(doc)) { - Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc); - } + Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc); - if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest dashboard - userDoc ? (CurrentUserUtils.ActiveDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc); - } + // this has the side-effect of setting the main container since we're assigning the active/guest dashboard + Doc.UserDoc() ? (CurrentUserUtils.ActiveDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc); + const state = CurrentUserUtils._urlState; - if (state.sharing === true && !userDoc) { + if (state.sharing === true && !Doc.UserDoc()) { DocServer.Control.makeReadOnly(); } else { fromHistory || HistoryUtil.pushState({ @@ -1054,7 +1070,7 @@ export class CurrentUserUtils { const upload = Utils.prepend("/uploadDoc"); const formData = new FormData(); const file = input.files && input.files[0]; - if (file && file.type === 'application/zip') { + if (file?.type === 'application/zip') { formData.append('file', file); formData.append('remap', "true"); const response = await fetch(upload, { method: "POST", body: formData }); @@ -1067,9 +1083,8 @@ export class CurrentUserUtils { } } } else if (input.files && input.files.length !== 0) { - const importDocs = CurrentUserUtils.MyImports; const disposer = OverlayView.ShowSpinner(); - DocListCastAsync(importDocs.data).then(async list => { + DocListCastAsync(CurrentUserUtils.MyImports.data).then(async list => { const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {}); if (results.length !== input.files?.length) { alert("Error uploading files - possibly due to unsupported file types"); -- cgit v1.2.3-70-g09d2 From 8eb794e233f905daaa5b9a25c6720e567512653e Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 23 Jun 2022 12:15:47 -0400 Subject: fixed : contextMenu . fixed some fitWidth issues for text where heights weren't working right --- src/client/documents/Documents.ts | 27 +++------ src/client/util/CurrentUserUtils.ts | 4 +- src/client/views/ContextMenu.tsx | 64 ++++++---------------- src/client/views/ContextMenuItem.tsx | 10 +--- src/client/views/nodes/DocumentView.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 3 +- 6 files changed, 33 insertions(+), 77 deletions(-) (limited to 'src/client/documents/Documents.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ad6a4dd3c..fc73deb36 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -363,7 +363,7 @@ export namespace Docs { layout: { view: FormattedTextBox, dataField: "text" }, options: { _height: 35, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, treeViewGrowsHorizontally: true, - links: "@links(self)" + forceReflow: true, links: "@links(self)" } }], [DocumentType.SEARCH, { @@ -400,7 +400,7 @@ export namespace Docs { }], [DocumentType.AUDIO, { layout: { view: AudioBox, dataField: defaultDataKey }, - options: { _height: 100, backgroundColor: "lightGray", links: "@links(self)" } + options: { _height: 100, backgroundColor: "lightGray", forceReflow: true, nativeDimModifiable: true, links: "@links(self)" } }], [DocumentType.REC, { layout: { view: VideoBox, dataField: defaultDataKey }, @@ -412,7 +412,7 @@ export namespace Docs { }], [DocumentType.MAP, { layout: { view: MapBox, dataField: defaultDataKey }, - options: { _height: 600, _width: 800, links: "@links(self)" } + options: { _height: 600, _width: 800, nativeDimModifiable: true, links: "@links(self)" } }], [DocumentType.IMPORT, { layout: { view: DirectoryImportBox, dataField: defaultDataKey }, @@ -450,11 +450,11 @@ export namespace Docs { }], [DocumentType.EQUATION, { layout: { view: EquationBox, dataField: defaultDataKey }, - options: { links: "@links(self)", hideResizeHandles: true, hideDecorationTitle: true } + options: { links: "@links(self)", nativeDimModifiable: true, hideResizeHandles: true, hideDecorationTitle: true } }], [DocumentType.FUNCPLOT, { layout: { view: FunctionPlotBox, dataField: defaultDataKey }, - options: { links: "@links(self)" } + options: { nativeDimModifiable: true, links: "@links(self)" } }], [DocumentType.BUTTON, { layout: { view: LabelBox, dataField: "onClick" }, @@ -493,7 +493,7 @@ export namespace Docs { }], [DocumentType.COMPARISON, { layout: { view: ComparisonBox, dataField: defaultDataKey }, - options: { clipWidth: 50, backgroundColor: "gray", targetDropAction: "alias", links: "@links(self)" } + options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: "gray", targetDropAction: "alias", links: "@links(self)" } }], [DocumentType.GROUPDB, { data: new List(), @@ -506,7 +506,7 @@ export namespace Docs { }], [DocumentType.DATAVIZ, { layout: { view: DataVizBox, dataField: defaultDataKey }, - options: { _fitWidth: true, _fitHeight: true, links: "@links(self)" } + options: { _fitWidth: true, nativeDimModifiable: true, links: "@links(self)" } }] ]); @@ -530,15 +530,6 @@ export namespace Docs { const prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix); // fetch the actual prototype documents from the server const actualProtos = Docs.newAccount ? {} : await DocServer.GetRefFields(prototypeIds); - if (!Docs.newAccount) { - Cast(actualProtos[DocumentType.WEB + suffix], Doc, null).nativeHeightUnfrozen = true; // to avoid having to recreate the DB - Cast(actualProtos[DocumentType.PDF + suffix], Doc, null).nativeHeightUnfrozen = true; - Cast(actualProtos[DocumentType.RTF + suffix], Doc, null).nativeHeightUnfrozen = true; - Cast(actualProtos[DocumentType.WEB + suffix], Doc, null).nativeDimModifiable = true; // to avoid having to recreate the DB - Cast(actualProtos[DocumentType.PDF + suffix], Doc, null).nativeDimModifiable = true; - Cast(actualProtos[DocumentType.RTF + suffix], Doc, null).nativeDimModifiable = true; - } - // update this object to include any default values: DocumentOptions for all prototypes prototypeIds.map(id => { const existing = actualProtos[id] as Doc; @@ -1273,8 +1264,8 @@ export namespace DocUtils { })) as ContextMenuProps[], icon: "sticky-note" }); - const documentList: ContextMenuProps[] = DocListCast(DocListCast(CurrentUserUtils.MyTools?.data).lastElement()?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({ - description: ":" + StrCast(dragDoc.title), + const documentList: ContextMenuProps[] = DocListCast(DocListCast(CurrentUserUtils.MyTools?.data)[0]?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({ + description: ":" + StrCast(dragDoc.title).replace("Untitled ",""), event: undoBatch((args: { x: number, y: number }) => { const newDoc = Doc.copyDragFactory(dragDoc); if (newDoc) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b9724051a..28d20c56b 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -282,13 +282,13 @@ export class CurrentUserUtils { }[] = [ {key: "Note", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _autoHeight: true }}, {key: "Collection", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 150, _height: 100 }}, - {key: "Equation", creator: opts => Docs.Create.EquationDocument(opts), opts: { _width: 300, _height: 35, _backgroundGridShow: true, }}, + {key: "Equation", creator: opts => Docs.Create.EquationDocument(opts), opts: { _width: 300, _height: 35, _fitWidth:false, _backgroundGridShow: true, }}, {key: "Webpage", creator: opts => Docs.Create.WebDocument("",opts), opts: { _width: 400, _height: 512, _nativeWidth: 850, useCors: true, }}, {key: "Comparison", creator: Docs.Create.ComparisonDocument, opts: { _width: 300, _height: 300 }}, {key: "Audio", creator: opts => Docs.Create.AudioDocument(nullAudio, opts),opts: { _width: 200, _height: 100, }}, {key: "Map", creator: opts => Docs.Create.MapDocument([], opts), opts: { _width: 800, _height: 600, _showSidebar: true, }}, {key: "Screengrab", creator: Docs.Create.ScreenshotDocument, opts: { _width: 400, _height: 200 }}, - {key: "WebCam", creator: opts => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, title: "recording", recording:true, system: true, cloneFieldFilter: new List(["system"]) }}, + {key: "WebCam", creator: opts => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, recording:true, system: true, cloneFieldFilter: new List(["system"]) }}, {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, }}, {key: "Script", creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }}, {key: "DataViz", creator: opts => Docs.Create.DataVizDocument(opts), opts: { _width: 300, _height: 300 }}, diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index e2f98de1e..9034cacb2 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -1,10 +1,10 @@ import React = require("react"); -import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from "./ContextMenuItem"; -import { observable, action, computed, runInAction, IReactionDisposer, reaction } from "mobx"; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, computed, IReactionDisposer, observable } from "mobx"; import { observer } from "mobx-react"; import "./ContextMenu.scss"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import Measure from "react-measure"; +import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from "./ContextMenuItem"; +import { Utils } from "../../Utils"; @observer export class ContextMenu extends React.Component { @@ -116,10 +116,6 @@ export class ContextMenu extends React.Component { this._defaultItem = item; } - getItems() { - return this._items; - } - static readonly buffer = 20; get pageX() { const x = this._pageX; @@ -199,37 +195,25 @@ export class ContextMenu extends React.Component { return eles; }; - return flattenItems(this._items, name => [name]); + return flattenItems(this._items.slice(), name => [name]); } @computed get flatItems(): OriginalMenuProps[] { return this.filteredItems.filter(item => !Array.isArray(item)) as OriginalMenuProps[]; } - @computed get filteredViews() { - const createGroupHeader = (contents: any) => { - return ( -
-
{contents}
-
- ); - }; - const createItem = (item: ContextMenuProps, selected: boolean) => ; - let itemIndex = 0; - return this.filteredItems.map(value => { - if (Array.isArray(value)) { - return createGroupHeader(value.join(" -> ")); - } else { - return createItem(value, itemIndex++ === this.selectedIndex); - } - }); - } - @computed get menuItems() { if (!this._searchString) { return this._items.map((item, ind) => ); } - return this.filteredViews; + return this.filteredItems.map((value, index) => + Array.isArray(value) ? +
+
{value.join(" -> ")}
+
+ : + + ); } @computed get itemsNeedSearch() { @@ -237,14 +221,8 @@ export class ContextMenu extends React.Component { } render() { - if (!this._display) { - return null; - } - const style = this._yRelativeToTop ? { left: this.pageX, top: this.pageY } : - { left: this.pageX, bottom: this.pageY }; - - const contents = ( - <> + return !this._display ? (null) : +
{!this.itemsNeedSearch ? (null) : @@ -253,17 +231,7 @@ export class ContextMenu extends React.Component { } {this.menuItems} - - ); - return ( - { this._width = r.offset.width; this._height = r.offset.height; })}> - {({ measureRef }) => ( -
- {contents} -
- )} -
- ); +
; } @action diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index 25d00f701..c136de1c3 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -28,11 +28,10 @@ export type ContextMenuProps = OriginalMenuProps | SubmenuProps; export class ContextMenuItem extends React.Component { @observable private _items: Array = []; @observable private overItem = false; - @observable private subRef = React.createRef(); - constructor(props: ContextMenuProps | SubmenuProps) { - super(props); - if ((this.props as SubmenuProps).subitems) { + componentDidMount() { + this._items.length = 0; + if ((this.props as SubmenuProps)?.subitems) { (this.props as SubmenuProps).subitems?.forEach(i => this._items.push(i)); } } @@ -78,9 +77,6 @@ export class ContextMenuItem extends React.Component diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5d2adc321..8d4fd376f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1251,7 +1251,7 @@ export class DocumentView extends React.Component { @computed get Xshift() { return this.effectiveNativeWidth ? (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2 : 0; } @computed get Yshift() { return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 ? (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2 : 0; } @computed get centeringX() { return this.props.dontCenter?.includes("x") ? 0 : this.Xshift; } - @computed get centeringY() { return this.fitWidth || this.props.dontCenter?.includes("y") ? 0 : this.Yshift; } + @computed get centeringY() { return this.props.dontCenter?.includes("y") ? 0 : this.Yshift; } toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.ContentScale, this.props.PanelWidth(), this.props.PanelHeight()); focus = (doc: Doc, options?: DocFocusOptions) => this.docView?.focus(doc, options); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 600730d87..d3dee3c89 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -862,6 +862,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if (nh) this.layoutDoc._nativeHeight = scrollHeight; } + @computed get contentScaling() { return Doc.NativeAspect(this.rootDoc, this.dataDoc, false) ? this.props.scaling?.() || 1 : 1;} componentDidMount() { !this.props.dontSelectOnLoad && this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. this._cachedLinks = DocListCast(this.Document.links); @@ -875,7 +876,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._disposers.componentHeights = reaction( // set the document height when one of the component heights changes and autoHeight is on () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, autoHeight: this.autoHeight, marginsHeight: this.autoHeightMargins }), ({ sidebarHeight, textHeight, autoHeight, marginsHeight }) => { - autoHeight && this.props.setHeight?.((this.props.scaling?.() || 1) * (marginsHeight + Math.max(sidebarHeight, textHeight))); + autoHeight && this.props.setHeight?.(this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight))); }, { fireImmediately: true }); this._disposers.links = reaction(() => DocListCast(this.dataDoc.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks newLinks => { -- cgit v1.2.3-70-g09d2 From e4ebdbbefb2696ea6b75464a93a5714ab0245135 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 23 Jun 2022 14:11:57 -0400 Subject: fixes for resizing native sized documents with fitwidth on/off and nativeHeightUnfrozen on/off --- src/client/documents/Documents.ts | 2 +- src/client/views/DocumentDecorations.tsx | 23 ++++++++++++----------- src/client/views/nodes/DocumentView.tsx | 3 +-- 3 files changed, 14 insertions(+), 14 deletions(-) (limited to 'src/client/documents/Documents.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index fc73deb36..f3f9bd1d9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -362,7 +362,7 @@ export namespace Docs { [DocumentType.RTF, { layout: { view: FormattedTextBox, dataField: "text" }, options: { - _height: 35, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, treeViewGrowsHorizontally: true, + _height: 35, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, treeViewGrowsHorizontally: true, forceReflow: true, links: "@links(self)" } }], diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 4247501bb..bef7c85a4 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -311,7 +311,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P move[1] = thisPt.y - this._snapY; this._snapX = thisPt.x; this._snapY = thisPt.y; - let dragBottom = false, dragRight = false, dragBotRight = false; + let dragBottom = false, dragRight = false, dragBotRight = false, dragTop = false; let dX = 0, dY = 0, dW = 0, dH = 0; switch (this._resizeHdlId.split(" ")[0]) { case "": break; @@ -329,7 +329,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P case "documentDecorations-topResizer": dY = -1; dH = -move[1]; - dragBottom = true; + dragTop = true; break; case "documentDecorations-bottomLeftResizer": dX = -1; @@ -361,27 +361,28 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P const doc = Document(docView.rootDoc); const nwidth = docView.nativeWidth; const nheight = docView.nativeHeight; - const docheight = doc._height || 0; - const docwidth = doc._width || 0; + let docheight = doc._height || 0; + let docwidth = doc._width || 0; const width = docwidth; let height = (docheight || (nheight / nwidth * width)); height = !height || isNaN(height) ? 20 : height; const scale = docView.props.ScreenToLocalTransform().Scale; - const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable; + const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable && ((!dragBottom && !dragTop) || doc.nativeHeightUnfrozen); if (nwidth && nheight) { - if (nwidth / nheight !== width / height && !dragBottom) { + if (nwidth / nheight !== width / height && !dragBottom && !dragTop) { height = nheight / nwidth * width; } - if (modifyNativeDim && !dragBottom) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction + if (modifyNativeDim && !dragBottom && !dragTop) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction if (Math.abs(dW) > Math.abs(dH)) dH = dW * nheight / nwidth; else dW = dH * nwidth / nheight; } } let actualdW = Math.max(width + (dW * scale), 20); let actualdH = Math.max(height + (dH * scale), 20); - const fixedAspect = (nwidth && nheight && !doc._fitWidth); + const fixedAspect = (nwidth && nheight && (!doc._fitWidth || doc.nativeHeightUnfrozen)); + console.log(fixedAspect); if (fixedAspect) { - if ((Math.abs(dW) > Math.abs(dH) && (!dragBottom || !modifyNativeDim)) || dragRight) { + if ((Math.abs(dW) > Math.abs(dH) && ((!dragBottom && !dragTop)|| !modifyNativeDim)) || dragRight) { if (dragRight && modifyNativeDim) { doc._nativeWidth = actualdW / (doc._width || 1) * Doc.NativeWidth(doc); } else { @@ -394,7 +395,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P doc._width = actualdW; } else { - if (dragBottom && (modifyNativeDim || + if ((dragBottom|| dragTop) && (modifyNativeDim || (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._fitWidth))) { // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height. But they are marked to allow their nativeHeight to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used) doc._nativeHeight = actualdH / (doc._height || 1) * Doc.NativeHeight(doc); doc._autoHeight = false; @@ -417,7 +418,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P dH && (doc._autoHeight = false); } doc.x = (doc.x || 0) + dX * (actualdW - docwidth); - doc.y = (doc.y || 0) + dY * (actualdH - docheight); + doc.y = (doc.y || 0) + (dragBottom ? 0: dY * (actualdH - docheight)); doc._lastModified = new DateField(); } const val = this._dragHeights.get(docView.layoutDoc); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 8d4fd376f..4c612891b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1249,7 +1249,7 @@ export class DocumentView extends React.Component { return this.props.PanelHeight(); } @computed get Xshift() { return this.effectiveNativeWidth ? (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2 : 0; } - @computed get Yshift() { return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 ? (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2 : 0; } + @computed get Yshift() { return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 && !this.layoutDoc.nativeHeightUnfrozen ? Math.max(0, (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2) : 0; } @computed get centeringX() { return this.props.dontCenter?.includes("x") ? 0 : this.Xshift; } @computed get centeringY() { return this.props.dontCenter?.includes("y") ? 0 : this.Yshift; } @@ -1343,7 +1343,6 @@ export class DocumentView extends React.Component { transition: this.props.dataTransition, position: this.props.Document.isInkMask ? "absolute" : undefined, transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`, - margin: this.fitWidth ? "auto" : undefined, width: isButton || isPresTreeElement ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`, height: isButton || this.props.forceAutoHeight ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` : `${100 * this.effectiveNativeHeight / this.effectiveNativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%`), -- cgit v1.2.3-70-g09d2 From 550b5e93097e0708199a9cf88714754d197b3b97 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 27 Jun 2022 12:48:54 -0400 Subject: fixed initialization issues with creating user documents unnecessarily. fixed updating doc icon templates to apply to documents already created. --- src/client/documents/Documents.ts | 10 +- src/client/util/CurrentUserUtils.ts | 64 ++++++----- src/client/util/LinkManager.ts | 123 +++++++++++---------- src/client/views/Main.tsx | 3 +- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/fields/Doc.ts | 2 +- 6 files changed, 104 insertions(+), 100 deletions(-) (limited to 'src/client/documents/Documents.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 84e6258ac..3780df5b9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -105,7 +105,6 @@ export class DocumentOptions { userColor?: STRt = new StrInfo("color associated with a Dash user (seen in header fields of shared documents)"); color?: STRt = new StrInfo("foreground color data doc"); backgroundColor?: STRt = new StrInfo("background color for data doc"); - _backgroundColor?: STRt = new StrInfo("background color for each template layout doc (overrides backgroundColor)"); _autoHeight?: BOOLt = new BoolInfo("whether document automatically resizes vertically to display contents"); _headerHeight?: NUMt = new NumInfo("height of document header used for displaying title"); _headerFontSize?: NUMt = new NumInfo("font size of header of custom notes"); @@ -328,6 +327,7 @@ export class DocumentOptions { text?: string; textTransform?: string; // is linear view expanded letterSpacing?: string; // is linear view expanded + iconTemplate?: string; // name of icon template style selectedIndex?: number; // which item in a linear view has been selected using the "thumb doc" ui clipboard?: Doc; searchQuery?: string; // for quersyBox @@ -1288,9 +1288,7 @@ export namespace DocUtils { const batch = UndoManager.StartBatch("makeCustomViewClicked"); runInAction(() => { doc.layoutKey = "layout_" + templateSignature; - if (doc[doc.layoutKey] === undefined) { - createCustomView(doc, creator, templateSignature, docLayoutTemplate); - } + createCustomView(doc, creator, templateSignature, docLayoutTemplate); }); batch.end(); return doc; @@ -1318,7 +1316,9 @@ export namespace DocUtils { const options = { title: "data", backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: - _height / 2, _showSidebar: false }; if (docLayoutTemplate) { - Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined); + if (docLayoutTemplate !== doc[customName]) { + Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined); + } } else { let fieldTemplate: Opt; if (doc.data instanceof RichTextField || typeof (doc.data) === "string") { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 5c77041e0..f585277d8 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -205,29 +205,37 @@ export class CurrentUserUtils { const reqdOpts = { title: "icon templates", _height: 75, system: true }; const templateIconsDoc = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts)); - const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: (opts:DocumentOptions) => Doc) => { + const makeIconTemplate = (type: DocumentType | undefined, templateField: string, opts:DocumentOptions) => { const iconFieldName = "icon" + (type ? "_" + type : ""); - return DocCast(templateIconsDoc[iconFieldName] ?? (templateIconsDoc[iconFieldName] = MakeTemplate(iconTemplate({onClick:deiconifyScript(), system: true}), true, iconFieldName, templateField))) ; + const curIcon = DocCast(templateIconsDoc[iconFieldName]); + let creator = labelBox; + switch (opts.iconTemplate) { + case DocumentType.IMG : creator = imageBox; break; + case DocumentType.FONTICON: creator = fontBox; break; + } + const allopts = {system: true, ...opts}; + return this.AssignScripts( (curIcon?.iconTemplate === opts.iconTemplate ? + this.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))), + {onClick:"deiconifyView(documentView)"}); }; - const deiconifyScript = () => ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }); - const labelBox = (opts: object) => Docs.Create.LabelDocument({ + const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({ textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts }); - const imageBox = (url: Opt, opts: object) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, _width: 360 / 4, _height: 270 / 4, _showTitle: "title", ...opts }); - const fontBox = (opts:DocumentOptions) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts }); + const imageBox = (opts: DocumentOptions, url?:string) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG, _width: 360 / 4, _height: 270 / 4, _showTitle: "title", ...opts }); + const fontBox = (opts:DocumentOptions, data?:string) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts }); const iconTemplates = [ - makeIconTemplate(undefined, "title", (opts) => labelBox({ ...opts, _backgroundColor: "dimgray" })), - makeIconTemplate(DocumentType.AUDIO, "title", (opts) => labelBox({ ...opts, _backgroundColor: "lightgreen" })), - makeIconTemplate(DocumentType.PDF, "title", (opts) => labelBox({ ...opts, _backgroundColor: "pink" })), - makeIconTemplate(DocumentType.WEB, "title", (opts) => labelBox({...opts, _backgroundColor: "brown" })), - makeIconTemplate(DocumentType.RTF, "text", (opts) => labelBox({ ...opts, _showTitle: "creationDate" })), - makeIconTemplate(DocumentType.IMG, "data", (opts) => imageBox("", { ...opts, _height: undefined, })), - makeIconTemplate(DocumentType.COL, "icon", (opts) => imageBox(undefined, opts)), - makeIconTemplate(DocumentType.VID, "icon", (opts) => imageBox(undefined, opts)), - makeIconTemplate(DocumentType.BUTTON, "data", (opts) => fontBox(opts)), + makeIconTemplate(undefined, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "dimgray"}), + makeIconTemplate(DocumentType.AUDIO, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "lightgreen"}), + makeIconTemplate(DocumentType.PDF, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "pink"}), + makeIconTemplate(DocumentType.WEB, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "brown"}), + makeIconTemplate(DocumentType.RTF, "text", { iconTemplate:DocumentType.LABEL, _showTitle: "creationDate"}), + makeIconTemplate(DocumentType.IMG, "data", { iconTemplate:DocumentType.IMG, _height: undefined}), + makeIconTemplate(DocumentType.COL, "icon", { iconTemplate:DocumentType.IMG}), + makeIconTemplate(DocumentType.VID, "icon", { iconTemplate:DocumentType.IMG}), + makeIconTemplate(DocumentType.BUTTON,"data", { iconTemplate:DocumentType.FONTICON}), //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now - makeIconTemplate("transcription" as any, "transcription", (opts) => labelBox({ ...opts, _backgroundColor: "orange" })), - // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})) + makeIconTemplate("transcription" as any, "transcription", { iconTemplate:DocumentType.LABEL, backgroundColor: "orange" }), + //makeIconTemplate(DocumentType.PDF, "icon", {iconTemplate:DocumentType.IMG}, (opts) => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", opts)) ].filter(d => d).map(d => d!); this.AssignOpts(DocCast(doc[field]), {}, iconTemplates); } @@ -779,23 +787,23 @@ export class CurrentUserUtils { /// Initializes all the default buttons for the top bar context menu static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") { - const ctxtMenuBtnsDoc = DocCast(doc[field]); + const reqdCtxtOpts = { title: "context menu buttons", flexGap: 0, childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }; + const ctxtMenuBtnsDoc = this.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined); const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => { const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title); if (!params.subMenu) { return this.setupContextMenuButton(params, menuBtnDoc); } else { - const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, title:"submenu", + const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: true, linearViewSubMenu: true, linearViewExpandable: true, }; return this.AssignScripts(this.AssignOpts(menuBtnDoc, reqdSubMenuOpts) ?? - this.linearButtonList(reqdSubMenuOpts, params.subMenu.map(sub => + (ctxtMenuBtnsDoc[StrCast(params.title)]= this.linearButtonList(reqdSubMenuOpts, params.subMenu.map(sub => this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title)) - )), undefined, params.funcs); + ))), undefined, params.funcs); } }); - const reqdCtxtOpts = { title: "context menu buttons", flexGap: 0, childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }; - return this.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, ctxtMenuBtns); + return this.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns); } /// collection of documents rendered in the overlay layer above all tabs and other UI @@ -957,12 +965,13 @@ export class CurrentUserUtils { this.AssignDocField(doc, "globalScriptDatabase", (opts) => Docs.Prototypes.MainScriptDocument(), {}); this.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents - setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500); if (doc.activeDashboard instanceof Doc) { // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme; } + new LinkManager(); + DocServer.UPDATE_SERVER_CACHE(); return doc; } static setupFieldInfos(doc:Doc, field="fieldInfos") { @@ -1009,12 +1018,7 @@ export class CurrentUserUtils { await Docs.Prototypes.initialize(); const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc; Docs.newAccount &&(userDoc.activePage = "home"); - const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId); - (await DocListCastAsync(Doc.LinkDBDoc()?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager - const a1 = await Cast(link?.anchor1, Doc, null); - const a2 = await Cast(link?.anchor2, Doc, null); - }); - return updated; + return this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId); }); } else { throw new Error("There should be a user id! Why does Dash think there isn't one?"); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 2100b1195..d51cd350d 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -1,7 +1,6 @@ -import { validationResult } from "express-validator/check"; import { action, observable, observe } from "mobx"; import { computedFn } from "mobx-utils"; -import { DirectLinksSym, Doc, DocListCast, Field, Opt } from "../../fields/Doc"; +import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from "../../fields/Doc"; import { List } from "../../fields/List"; import { ProxyField } from "../../fields/Proxy"; import { BoolCast, Cast, StrCast } from "../../fields/Types"; @@ -35,72 +34,74 @@ export class LinkManager { constructor() { LinkManager._instance = this; this.createLinkrelationshipLists(); - setTimeout(() => { - LinkManager.userLinkDBs = []; - const addLinkToDoc = (link: Doc) => { - const a1Prom = link?.anchor1; - const a2Prom = link?.anchor2; - Promise.all([a1Prom, a2Prom]).then((all) => { - const a1 = all[0]; - const a2 = all[1]; - const a1ProtoProm = (link?.anchor1 as Doc)?.proto; - const a2ProtoProm = (link?.anchor2 as Doc)?.proto; - Promise.all([a1ProtoProm, a2ProtoProm]).then(action((all) => { - if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) { - Doc.GetProto(a1)[DirectLinksSym].add(link); - Doc.GetProto(a2)[DirectLinksSym].add(link); - Doc.GetProto(link)[DirectLinksSym].add(link); - } - })); - }); - }; - const remLinkFromDoc = (link: Doc) => { - const a1 = link?.anchor1; - const a2 = link?.anchor2; - Promise.all([a1, a2]).then(action(() => { + LinkManager.userLinkDBs = []; + const addLinkToDoc = (link: Doc) => { + const a1Prom = link?.anchor1; + const a2Prom = link?.anchor2; + Promise.all([a1Prom, a2Prom]).then((all) => { + const a1 = all[0]; + const a2 = all[1]; + const a1ProtoProm = (link?.anchor1 as Doc)?.proto; + const a2ProtoProm = (link?.anchor2 as Doc)?.proto; + Promise.all([a1ProtoProm, a2ProtoProm]).then(action((all) => { if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) { - Doc.GetProto(a1)[DirectLinksSym].delete(link); - Doc.GetProto(a2)[DirectLinksSym].delete(link); - Doc.GetProto(link)[DirectLinksSym].delete(link); + Doc.GetProto(a1)[DirectLinksSym].add(link); + Doc.GetProto(a2)[DirectLinksSym].add(link); + Doc.GetProto(link)[DirectLinksSym].add(link); } })); - }; - const watchUserLinkDB = (userLinkDBDoc: Doc) => { - LinkManager.links.push(...DocListCast(userLinkDBDoc.data)); - const toRealField = (field: Field) => field instanceof ProxyField ? field.value() : field; // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields - observe(userLinkDBDoc.data as Doc, change => { // observe pushes/splices on a user link DB 'data' field (should only happen for local changes) - switch (change.type as any) { - case "splice": - (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link))); - (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link))); - break; - case "update": //let oldValue = change.oldValue; - } - }, true); - observe(userLinkDBDoc, "data", // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link) - change => { - switch (change.type as any) { - case "update": - Promise.all([...(change.oldValue as any as Doc[] || []), ...(change.newValue as any as Doc[] || [])]).then(doclist => { - const oldDocs = doclist.slice(0, (change.oldValue as any as Doc[] || []).length); - const newDocs = doclist.slice((change.oldValue as any as Doc[] || []).length, doclist.length); - - const added = newDocs?.filter(link => !(oldDocs || []).includes(link)); - const removed = oldDocs?.filter(link => !(newDocs || []).includes(link)); - added?.forEach((link: any) => addLinkToDoc(toRealField(link))); - removed?.forEach((link: any) => remLinkFromDoc(toRealField(link))); - }); - } - }, true); - }; - observe(LinkManager.userLinkDBs, change => { + }); + }; + const remLinkFromDoc = (link: Doc) => { + const a1 = link?.anchor1; + const a2 = link?.anchor2; + Promise.all([a1, a2]).then(action(() => { + if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) { + Doc.GetProto(a1)[DirectLinksSym].delete(link); + Doc.GetProto(a2)[DirectLinksSym].delete(link); + Doc.GetProto(link)[DirectLinksSym].delete(link); + } + })); + }; + const watchUserLinkDB = (userLinkDBDoc: Doc) => { + LinkManager.links.push(...DocListCast(userLinkDBDoc.data)); + const toRealField = (field: Field) => field instanceof ProxyField ? field.value() : field; // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields + observe(userLinkDBDoc.data as Doc, change => { // observe pushes/splices on a user link DB 'data' field (should only happen for local changes) switch (change.type as any) { - case "splice": (change as any).added.forEach(watchUserLinkDB); break; + case "splice": + (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link))); + (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link))); + break; case "update": //let oldValue = change.oldValue; } }, true); - LinkManager.addLinkDB(Doc.LinkDBDoc()); - }); + observe(userLinkDBDoc, "data", // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link) + change => { + switch (change.type as any) { + case "update": + Promise.all([...(change.oldValue as any as Doc[] || []), ...(change.newValue as any as Doc[] || [])]).then(doclist => { + const oldDocs = doclist.slice(0, (change.oldValue as any as Doc[] || []).length); + const newDocs = doclist.slice((change.oldValue as any as Doc[] || []).length, doclist.length); + + const added = newDocs?.filter(link => !(oldDocs || []).includes(link)); + const removed = oldDocs?.filter(link => !(newDocs || []).includes(link)); + added?.forEach((link: any) => addLinkToDoc(toRealField(link))); + removed?.forEach((link: any) => remLinkFromDoc(toRealField(link))); + }); + } + }, true); + }; + observe(LinkManager.userLinkDBs, change => { + switch (change.type as any) { + case "splice": (change as any).added.forEach(watchUserLinkDB); break; + case "update": //let oldValue = change.oldValue; + } + }, true); + LinkManager.addLinkDB(Doc.LinkDBDoc()); + DocListCastAsync(Doc.LinkDBDoc()?.data).then(dblist => dblist?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager + const a1 = await Cast(link?.anchor1, Doc, null); + const a2 = await Cast(link?.anchor2, Doc, null); + })); } diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 49c2dcf34..17841c055 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -10,7 +10,6 @@ import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { LinkManager } from "../util/LinkManager"; import { RecordingApi } from "../util/RecordingApi"; import { CollectionView } from "./collections/CollectionView"; -import { DashboardView } from './DashboardView'; import { MainView } from "./MainView"; AssignAllExtensions(); @@ -24,6 +23,7 @@ AssignAllExtensions(); await CurrentUserUtils.loadUserDocument(info.id); } else { await Docs.Prototypes.initialize(); + new LinkManager(); } document.getElementById('root')!.addEventListener('wheel', event => { if (event.ctrlKey) { @@ -37,7 +37,6 @@ AssignAllExtensions(); d.setTime(d.getTime() + (100 * 24 * 60 * 60 * 1000)); const expires = "expires=" + d.toUTCString(); document.cookie = `loadtime=${loading};${expires};path=/`; - new LinkManager(); new RecordingApi; ReactDOM.render(, document.getElementById('root')); })(); \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 081a1a924..17f1106d0 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -517,7 +517,7 @@ export class MarqueeView extends React.Component Date: Tue, 28 Jun 2022 12:23:44 -0400 Subject: cleaned up some more currentUserUtils - fixed some issues with prev/next key frame appearing. --- src/client/documents/Documents.ts | 29 +++++- src/client/util/CurrentUserUtils.ts | 111 ++++----------------- .../views/collections/CollectionDockingView.tsx | 37 +++++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 19 +++- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/topbar/TopBar.tsx | 3 +- src/fields/Doc.ts | 25 +---- 7 files changed, 97 insertions(+), 129 deletions(-) (limited to 'src/client/documents/Documents.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3780df5b9..4690d856d 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -13,7 +13,7 @@ import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../fields/Types"; import { AudioField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from "../../fields/URLField"; -import { SharingPermissions } from "../../fields/util"; +import { inheritParentAcls, SharingPermissions } from "../../fields/util"; import { Upload } from "../../server/SharedMediaTypes"; import { aggregateBounds, OmitKeys, Utils } from "../../Utils"; import { YoutubeBox } from "../apis/youtube/YoutubeBox"; @@ -1266,7 +1266,7 @@ export namespace DocUtils { const documentList: ContextMenuProps[] = DocListCast(DocListCast(CurrentUserUtils.MyTools?.data)[0]?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({ description: ":" + StrCast(dragDoc.title).replace("Untitled ",""), event: undoBatch((args: { x: number, y: number }) => { - const newDoc = Doc.copyDragFactory(dragDoc); + const newDoc = DocUtils.copyDragFactory(dragDoc); if (newDoc) { newDoc.author = Doc.CurrentUserEmail; newDoc.x = x; @@ -1479,9 +1479,34 @@ export namespace DocUtils { } return generatedDocuments; } + + // copies the specified drag factory document + export function copyDragFactory(dragFactory: Doc) { + if (!dragFactory) return undefined; + const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true); + ndoc && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, "data", Doc.GetProto(ndoc)); + if (ndoc && dragFactory["dragFactory-count"] !== undefined) { + dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; + Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true); + } + + if (ndoc && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc); + + return ndoc; + } + export function delegateDragFactory(dragFactory: Doc) { + const ndoc = Doc.MakeDelegateWithProto(dragFactory); + if (ndoc && dragFactory["dragFactory-count"] !== undefined) { + dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; + Doc.GetProto(ndoc).title = ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(); + } + return ndoc; + } } ScriptingGlobals.add("Docs", Docs); +ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return DocUtils.copyDragFactory(dragFactory); }); +ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return DocUtils.delegateDragFactory(dragFactory); }); ScriptingGlobals.add(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: "child of " + proto.title }); return d; }); ScriptingGlobals.add(function generateLinkTitle(self: Doc) { const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null).title : ""; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 6e115ea3c..2a0702a58 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -755,8 +755,8 @@ export class CurrentUserUtils { CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, CollectionViewType.Grid]), title: "Perspective", toolTip: "View", width: 100,btnType: ButtonType.DropdownList,ignoreClick: true, scripts: { script: 'setView(value, _readOnly_)'}}, - { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'}}, - { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'}}, + { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode() || !selectedDocumentType(undefined, "freeform")'}}, + { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode() || !selectedDocumentType(undefined, "freeform")'}}, { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, scripts: { script: 'setBackgroundColor(value, _readOnly_)'},funcs: {hidden: '!selectedDocumentType()'}}, // Only when a document is selected { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, scripts: { script: 'setHeaderColor(value, _readOnly_)'}, funcs: {hidden: '!selectedDocumentType()'}}, { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, scripts: { onClick: 'toggleOverlay(_readOnly_)'}, funcs: {hidden: '!selectedDocumentType(undefined, "freeform", true)'}}, // Only when floating document is selected in freeform @@ -1028,8 +1028,10 @@ export class CurrentUserUtils { public static _urlState: HistoryUtil.DocUrl; - public static openDashboard = (doc: Doc, fromHistory = false) => { - if (!doc) return; + /// opens a dashboard as the ActiveDashboard (and adds the dashboard to the users list of dashboards if it's not already there). + /// this also sets the readonly state of the dashboard based on the current mode of dash (from its url) + public static openDashboard = (doc: Doc|undefined, fromHistory = false) => { + if (!doc) return false; CurrentUserUtils.MainDocId = doc[Id]; Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc); @@ -1097,46 +1099,15 @@ export class CurrentUserUtils { }; input.click(); } + + public static snapshotDashboard() { return CollectionDockingView.TakeSnapshot(CurrentUserUtils.ActiveDashboard); } - public static CaptureDashboardThumbnail() { - const activeDashboard = CurrentUserUtils.ActiveDashboard; - const docView = CollectionDockingView.Instance.props.DocumentView?.(); - const content = docView?.ContentDiv; - if (docView && content && activeDashboard) { - const _width = Number(getComputedStyle(content).width.replace("px","")); - const _height = Number(getComputedStyle(content).height.replace("px","")); - return CollectionFreeFormView.UpdateIcon( - docView.layoutDoc[Id] + "-icon" + (new Date()).getTime(), - content, - _width, _height, - _width, _height, 0, 1, true, docView.layoutDoc[Id] + "-icon", - (iconFile, _nativeWidth, _nativeHeight) => { - const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: docView.rootDoc.title+"-icon", _width, _height, _nativeWidth, _nativeHeight}); - const proto = Cast(img.proto, Doc, null)!; - proto["data-nativeWidth"] = _width; - proto["data-nativeHeight"] = _height; - Doc.GetProto(activeDashboard).thumb = img; - }); - } - - } - - public static async snapshotDashboard() { - if (CurrentUserUtils.ActiveDashboard) { - const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard); - Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy); - CurrentUserUtils.openDashboard(copy); - } - } - - public static closeActiveDashboard = () => { - CurrentUserUtils.ActiveDashboard = undefined; - } + public static closeActiveDashboard = () => { CurrentUserUtils.ActiveDashboard = undefined; } public static removeDashboard = async (dashboard:Doc) => { const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data); if (dashboards?.length) { - if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard)!); + if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard)); Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard); if (!dashboards.length) CurrentUserUtils.ActivePage = "home"; } @@ -1214,14 +1185,6 @@ export class CurrentUserUtils { public static get ActiveTool(): InkTool { return StrCast(Doc.UserDoc().activeTool, InkTool.None) as InkTool; } } -ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) { - const copy = Doc.copyDragFactory(dragFactory); - if (copy) { - CollectionDockingView.AddSplit(copy, "right"); - const view = DocumentManager.Instance.getFirstDocumentView(copy); - view && SelectionManager.SelectView(view, false); - } -}); ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs"); ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode"); ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering"); @@ -1232,52 +1195,14 @@ ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.creat ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)"); ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { SharingManager.Instance.open(undefined, dashboard); }, "opens sharing dialog for Dashboard"); -ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) { CurrentUserUtils.removeDashboard(dashboard); }, "Remove Dashboard from Dashboards"); -ScriptingGlobals.add(function addToDashboards(dashboard: Doc) { - const dashboardAlias = Doc.MakeAlias(dashboard); - Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias); - CurrentUserUtils.openDashboard(dashboardAlias); -}, - "adds Dashboard to set of Dashboards"); - -ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkParent?: boolean) { - let selected = SelectionManager.Docs().length ? SelectionManager.Docs()[0] : undefined; - if (selected && checkParent) { - const parentDoc: Doc = Cast(selected.context, Doc, null); - selected = parentDoc; - } - if (selected && docType && selected.type === docType) return true; - else if (selected && colType && selected.viewType === colType) return true; - else if (selected && !colType && !docType) return true; - else return false; +ScriptingGlobals.add(function removeDashboard(dashboard: Doc) { CurrentUserUtils.removeDashboard(dashboard); }, "Remove Dashboard from Dashboards"); +ScriptingGlobals.add(function addToDashboards(dashboard: Doc) { CurrentUserUtils.openDashboard( Doc.MakeAlias(dashboard)); }, "adds Dashboard to set of Dashboards"); +ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkContext?: boolean) { + let selected = (sel => checkContext ? DocCast(sel?.context) : sel)(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement()); + return docType ? selected?.type === docType : colType ? selected?.viewType === colType : true; }); ScriptingGlobals.add(function makeTopLevelFolder() { - const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true }); - TreeView._editTitleOnLoad = { id: folder[Id], parent: undefined }; - return Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", folder); -}); -ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { - if (readOnly) return; - const sel = SelectionManager.Views()[0]; - const col = (sel.ComponentView as CollectionFreeFormView); - const currentFrame = Cast(sel.props.Document._currentFrame, "number", null); - if (currentFrame === undefined) { - sel.props.Document._currentFrame = 0; - CollectionFreeFormDocumentView.setupKeyframes(col.childDocs, 0); - } - CollectionFreeFormDocumentView.updateKeyframe(col.childDocs, currentFrame || 0); - sel.rootDoc._currentFrame = Math.max(0, (currentFrame || 0) + 1); - sel.rootDoc.lastFrame = Math.max(NumCast(sel.rootDoc._currentFrame), NumCast(sel.rootDoc.lastFrame)); -}); -ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) { - if (readOnly) return; - const sel = SelectionManager.Views()[0]; - const col = (sel.ComponentView as CollectionFreeFormView); - const currentFrame = Cast(sel.props.Document._currentFrame, "number", null); - if (currentFrame === undefined) { - sel.props.Document._currentFrame = 0; - CollectionFreeFormDocumentView.setupKeyframes(col.childDocs, 0); - } - CollectionFreeFormDocumentView.gotoKeyframe(col.childDocs.slice()); - sel.rootDoc._currentFrame = Math.max(0, (currentFrame || 0) - 1); + TreeView._editTitleOnLoad = { id: Utils.GenerateGuid(), parent: undefined }; + const opts = { title: "Untitled folder", _stayInCollection: true, isFolder: true }; + return Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id)); }); \ No newline at end of file diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 07fcd6a7d..0830b6fdf 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -4,12 +4,12 @@ import { action, IReactionDisposer, observable, reaction, runInAction } from "mo import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import * as GoldenLayout from "../../../client/goldenLayout"; -import { DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc"; +import { Doc, DocListCast, Opt } from "../../../fields/Doc"; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; -import { listSpec } from '../../../fields/Schema'; import { Cast, NumCast, StrCast } from "../../../fields/Types"; +import { ImageField } from '../../../fields/URLField'; import { inheritParentAcls } from '../../../fields/util'; import { emptyFunction, incrementTitleCopy } from '../../../Utils'; import { DocServer } from "../../DocServer"; @@ -19,14 +19,15 @@ import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { DragManager } from "../../util/DragManager"; import { InteractionUtils } from '../../util/InteractionUtils'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; +import { SelectionManager } from '../../util/SelectionManager'; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { LightboxView } from '../LightboxView'; import "./CollectionDockingView.scss"; +import { CollectionFreeFormView } from './collectionFreeForm'; import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; import { CollectionViewType } from './CollectionView'; import { TabDocView } from './TabDocView'; import React = require("react"); -import { SelectionManager } from '../../util/SelectionManager'; const _global = (window /* browser */ || global /* node */) as any; @observer @@ -373,13 +374,34 @@ export class CollectionDockingView extends CollectionSubView() { } } - public static async Copy(doc: Doc, clone = false) { + public CaptureThumbnail() { + const content = this.props.DocumentView?.()?.ContentDiv; + if (content) { + const _width = Number(getComputedStyle(content).width.replace("px","")); + const _height = Number(getComputedStyle(content).height.replace("px","")); + return CollectionFreeFormView.UpdateIcon( + this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), + content, + _width, _height, + _width, _height, 0, 1, true, this.layoutDoc[Id] + "-icon", + (iconFile, _nativeWidth, _nativeHeight) => { + const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: this.rootDoc.title+"-icon", _width, _height, _nativeWidth, _nativeHeight}); + const proto = Cast(img.proto, Doc, null)!; + proto["data-nativeWidth"] = _width; + proto["data-nativeHeight"] = _height; + this.dataDoc.thumb = img; + }); + } + + } + public static async TakeSnapshot(doc: Doc|undefined, clone = false) { + if (!doc) return undefined; let json = StrCast(doc.dockingConfig); if (clone) { - const cloned = (await Doc.MakeClone(doc)); + const cloned = await Doc.MakeClone(doc); Array.from(cloned.map.entries()).map(entry => json = json.replace(entry[0], entry[1][Id])); Doc.GetProto(cloned.clone).dockingConfig = json; - return cloned.clone; + return CurrentUserUtils.openDashboard(cloned.clone); } const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g); const origtabids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) || []; @@ -395,7 +417,8 @@ export class CollectionDockingView extends CollectionSubView() { json = json.replace(origtab[Id], newtab[Id]); return newtab; }); - return Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) }); + const copy = Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) }); + return CurrentUserUtils.openDashboard(await copy); } @action diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 3c2047db7..b9da4faa4 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -153,6 +153,21 @@ export class CollectionFreeFormView extends CollectionSubView { + const currentFrame = Cast(this.Document._currentFrame, "number", null); + if (currentFrame === undefined) { + this.Document._currentFrame = 0; + CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0); + } + if (back) { + CollectionFreeFormDocumentView.gotoKeyframe(this.childDocs.slice()); + this.Document._currentFrame = Math.max(0, (currentFrame || 0) - 1); + } else { + CollectionFreeFormDocumentView.updateKeyframe(this.childDocs, currentFrame || 0); + this.Document._currentFrame = Math.max(0, (currentFrame || 0) + 1); + this.Document.lastFrame = Math.max(NumCast(this.Document._currentFrame), NumCast(this.Document.lastFrame)); + } + } @action setKeyFrameEditing = (set: boolean) => this._keyframeEditing = set; getKeyFrameEditing = () => this._keyframeEditing; onBrowseClickHandler = () => this.props.onBrowseClick?.() || ScriptCast(this.layoutDoc.onBrowseClick); @@ -2086,4 +2101,6 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY }); Doc.linkFollowHighlight(dv?.props.Document, false); } -ScriptingGlobals.add(CollectionBrowseClick); \ No newline at end of file +ScriptingGlobals.add(CollectionBrowseClick); +ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(); }); +ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) { !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true); }); \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 17f1106d0..ab8a34d5a 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -156,7 +156,7 @@ export class MarqueeView extends React.Component { - CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => { + CollectionDockingView.Instance.CaptureThumbnail()?.then(() => { CurrentUserUtils.ActivePage = "home"; CurrentUserUtils.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use }); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index a5d952176..b30ca644d 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -23,7 +23,7 @@ import { listSpec } from "./Schema"; import { ComputedField, ScriptField } from "./ScriptField"; import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types"; import { AudioField, ImageField, MapField, PdfField, VideoField, WebField } from "./URLField"; -import { deleteProperty, GetEffectiveAcl, getField, getter, inheritParentAcls, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util"; +import { deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util"; import JSZip = require("jszip"); export namespace Field { @@ -1242,27 +1242,6 @@ export namespace Doc { return !curPres ? false : DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1; } - export function copyDragFactory(dragFactory: Doc) { - const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true); - ndoc && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, "data", Doc.GetProto(ndoc)); - if (ndoc && dragFactory["dragFactory-count"] !== undefined) { - dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; - Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true); - } - - if (ndoc && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc); - - return ndoc; - } - export function delegateDragFactory(dragFactory: Doc) { - const ndoc = Doc.MakeDelegateWithProto(dragFactory); - if (ndoc && dragFactory["dragFactory-count"] !== undefined) { - dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; - Doc.GetProto(ndoc).title = ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(); - } - return ndoc; - } - export function toIcon(doc?: Doc, isOpen?: boolean) { switch (StrCast(doc?.type)) { case DocumentType.IMG: return "image"; @@ -1454,8 +1433,6 @@ ScriptingGlobals.add(function getProto(doc: any) { return Doc.GetProto(doc); }); ScriptingGlobals.add(function getDocTemplate(doc?: any) { return Doc.getDocTemplate(doc); }); ScriptingGlobals.add(function getAlias(doc: any) { return Doc.MakeAlias(doc); }); ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); }); -ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return Doc.copyDragFactory(dragFactory); }); -ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return Doc.delegateDragFactory(dragFactory); }); ScriptingGlobals.add(function copyField(field: any) { return Field.Copy(field); }); ScriptingGlobals.add(function docList(field: any) { return DocListCast(field); }); ScriptingGlobals.add(function addDocToList(doc: Doc, field: string, added:Doc) { return Doc.AddDocToList(doc,field, added); }); -- cgit v1.2.3-70-g09d2