aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/CurrentUserUtils.ts161
-rw-r--r--src/client/util/DocumentManager.ts104
-rw-r--r--src/client/util/DragManager.ts71
-rw-r--r--src/client/util/DropConverter.ts23
-rw-r--r--src/client/util/HypothesisUtils.ts18
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx2
-rw-r--r--src/client/util/LinkFollower.ts9
-rw-r--r--src/client/util/LinkManager.ts16
-rw-r--r--src/client/util/Scripting.ts5
-rw-r--r--src/client/util/ScrollBox.tsx24
-rw-r--r--src/client/util/SearchUtil.ts214
-rw-r--r--src/client/util/SelectionManager.ts9
-rw-r--r--src/client/util/SettingsManager.tsx17
-rw-r--r--src/client/util/UndoManager.ts2
14 files changed, 237 insertions, 438 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 4391e87d6..84bed08fa 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -14,10 +14,10 @@ import { SetCachedGroups, SharingPermissions } from "../../fields/util";
import { GestureUtils } from "../../pen-gestures/GestureUtils";
import { DocServer } from "../DocServer";
import { CollectionViewType, DocumentType } from "../documents/DocumentTypes";
-import { DocUtils, Docs, DocumentOptions, FInfo } from "../documents/Documents";
+import { DocUtils, Docs, DocumentOptions, FInfo, FInfoFieldType } from "../documents/Documents";
import { DashboardView } from "../views/DashboardView";
import { OverlayView } from "../views/OverlayView";
-import { TreeViewType } from "../views/collections/CollectionTreeView";
+import { CollectionTreeView, TreeViewType } from "../views/collections/CollectionTreeView";
import { Colors } from "../views/global/globalEnums";
import { media_state } from "../views/nodes/AudioBox";
import { OpenWhere } from "../views/nodes/DocumentView";
@@ -61,49 +61,16 @@ export let resolvedPorts: { server: number, socket: number };
export class CurrentUserUtils {
// initializes experimental advanced template views - slideView, headerView
- static setupExperimentalTemplateButtons(doc: Doc, tempDocs?:Doc) {
- const requiredTypeNameFields:{btnOpts:DocumentOptions, templateOpts:DocumentOptions, template:(opts:DocumentOptions) => Doc}[] = [
- {
- btnOpts: { title: "slide", icon: "address-card" },
- templateOpts: { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, isSystem: true },
- template: (opts:DocumentOptions) => Docs.Create.MultirowDocument(
- [
- Docs.Create.MulticolumnDocument([], { title: "hero", _height: 200, isSystem: true }),
- Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true, _height: 100, isSystem: true, _text_fontFamily: StrCast(Doc.UserDoc().fontFamily), _text_fontSize: StrCast(Doc.UserDoc().fontSize) })
- ], opts)
- },
- {
- btnOpts: { title: "mobile", icon: "mobile" },
- templateOpts: { title: "NEW MOBILE BUTTON", onClick: undefined, },
- template: (opts:DocumentOptions) => this.mobileButton(opts,
- [this.createToolButton({ ignoreClick: true, icon: "mobile", backgroundColor: "transparent" }),
- this.mobileTextContainer({},
- [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])
- ]
- )
- },
- ];
- const requiredTypes = requiredTypeNameFields.map(({ btnOpts, template, templateOpts }) => {
- const tempBtn = DocListCast(tempDocs?.data)?.find(doc => doc.title === btnOpts.title);
- const reqdScripts = { onDragStart: '{ return copyDragFactory(this.dragFactory,this.openFactoryAsDelegate); }' };
- const assignBtnAndTempOpts = (templateBtn:Opt<Doc>, btnOpts:DocumentOptions, templateOptions:DocumentOptions) => {
- if (templateBtn) {
- DocUtils.AssignOpts(templateBtn,btnOpts);
- DocUtils.AssignDocField(templateBtn, "dragFactory", opts => template(opts), templateOptions);
- }
- return templateBtn;
- };
- return DocUtils.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? this.createToolButton( {...btnOpts, dragFactory: MakeTemplate(template(templateOpts))}), reqdScripts);
- });
-
+ static setupUserDocumentCreatorButtons(doc: Doc, userDocTemplates: Opt<Doc>) {
+ const userTemplates = DocListCast(userDocTemplates?.data).filter(doc => !Doc.IsSystem(doc));
const reqdOpts:DocumentOptions = {
- title: "Experimental Tools", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true,
+ title: "User Tools", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true, hidden: false,
_dragOnlyWithinContainer: true, _layout_hideContextMenu: true, isSystem: true, _forceActive: true,
_layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
};
const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
- const reqdFuncs = { hidden: "IsNoviceMode()" };
- return DocUtils.AssignScripts(DocUtils.AssignOpts(tempDocs, reqdOpts, requiredTypes) ?? Docs.Create.MasonryDocument(requiredTypes, reqdOpts), reqdScripts, reqdFuncs);
+ const reqdFuncs = { /* hidden: "IsNoviceMode()" */ };
+ return DocUtils.AssignScripts(DocUtils.AssignOpts(userDocTemplates, reqdOpts, userTemplates) ?? Docs.Create.MasonryDocument(userTemplates, reqdOpts), reqdScripts, reqdFuncs);
}
/// Initializes templates for editing click funcs of a document
@@ -111,7 +78,7 @@ export class CurrentUserUtils {
const tempClicks = DocCast(doc[field]);
const reqdClickOpts:DocumentOptions = {_width: 300, _height:200, isSystem: true};
const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [
- { opts: { title: "Open In Target", targetScriptKey: "onChildClick"}, script: "docCastAsync(documentView?.props.docViewPath().lastElement()?.Document.target).then((target) => target && (target.proto.data = new List([self])))"},
+ { opts: { title: "Open In Target", targetScriptKey: "onChildClick"}, script: "docCastAsync(documentView?.containerViewPath().lastElement()?.Document.target).then((target) => target && (target.proto.data = new List([this])))"},
{ opts: { title: "Open Detail On Right", targetScriptKey: "onChildDoubleClick"}, script: `openDoc(this.doubleClickView.${OpenWhere.addRight})`}];
const reqdClickList = reqdTempOpts.map(opts => {
const allOpts = {...reqdClickOpts, ...opts.opts};
@@ -148,7 +115,7 @@ export class CurrentUserUtils {
static setupNoteTemplates(doc: Doc, field="template_notes") {
const tempNotes = DocCast(doc[field]);
const reqdTempOpts:DocumentOptions[] = [
- { noteType: "Note", backgroundColor: "yellow", icon: "sticky-note"},
+ { noteType: "Postit", backgroundColor: "yellow", icon: "sticky-note"},
{ noteType: "Idea", backgroundColor: "pink", icon: "lightbulb" },
{ noteType: "Topic", backgroundColor: "lightblue", icon: "book-open" }];
const reqdNoteList = reqdTempOpts.map(opts => {
@@ -192,7 +159,7 @@ export class CurrentUserUtils {
{onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView)", });
};
const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({
- textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 24, layout_borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
+ textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...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, _layout_showTitle: "title", ...opts });
const fontBox = (opts:DocumentOptions, data?:string) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts });
@@ -201,7 +168,7 @@ export class CurrentUserUtils {
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, _layout_showTitle: "author_date"}),
+ makeIconTemplate(DocumentType.RTF, "text", { iconTemplate:DocumentType.LABEL, _layout_showTitle: "title"}),
makeIconTemplate(DocumentType.IMG, "data", { iconTemplate:DocumentType.IMG, _height: undefined}),
makeIconTemplate(DocumentType.COL, "icon", { iconTemplate:DocumentType.IMG}),
makeIconTemplate(DocumentType.COL, "icon", { iconTemplate:DocumentType.IMG}),
@@ -257,6 +224,16 @@ export class CurrentUserUtils {
MakeTemplate(Doc.GetProto(header), true, "Untitled Header");
return header;
}
+ const slideView = (opts:DocumentOptions) => {
+ const slide = Docs.Create.MultirowDocument(
+ [
+ Docs.Create.MulticolumnDocument([], { title: "hero", _height: 200, isSystem: true }),
+ Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true, _height: 100, isSystem: true, _text_fontFamily: StrCast(Doc.UserDoc().fontFamily), _text_fontSize: StrCast(Doc.UserDoc().fontSize) })
+ ], opts);
+
+ MakeTemplate(Doc.GetProto(slide), true, "Untitled Slide View");
+ return slide;
+ }
const emptyThings:{key:string, // the field name where the empty thing will be stored
opts:DocumentOptions, // the document options that are required for the empty thing
funcs?:{[key:string]: any}, // computed fields that are rquired for the empth thing
@@ -275,15 +252,16 @@ export class CurrentUserUtils {
{key: "Map", creator: opts => Docs.Create.MapDocument([], opts), opts: { _width: 800, _height: 600, _layout_fitWidth: 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, recording:true, isSystem: true, cloneFieldFilter: new List<string>(["isSystem"]) }},
- {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, waitForDoubleClickToClick: 'never'}, scripts: {onClick: FollowLinkScript()?.script.originalScript ?? ""}},
+ {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, title_custom: true, waitForDoubleClickToClick: 'never'}, scripts: {onClick: FollowLinkScript()?.script.originalScript ?? ""}},
{key: "Script", creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }},
{key: "DataViz", creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts), opts: { _width: 300, _height: 300 }},
{key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _layout_autoHeight: true, treeView_HideUnrendered: true}},
- {key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: "embed" as dropActionType, treeView_HideTitle: true, _layout_fitWidth:true, layout_boxShadow: "0 0" }},
+ {key: "ViewSlide", creator: slideView, opts: { _width: 400, _height: 300, _xMargin: 3, _yMargin: 3,}},
+ {key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: dropActionType.embed, treeView_HideTitle: true, _layout_fitWidth:true, layout_boxShadow: "0 0" }},
{key: "Tab", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }},
{key: "Slide", creator: opts => Docs.Create.TreeDocument([], opts), opts: { _width: 300, _height: 200, _type_collection: CollectionViewType.Tree,
treeView_HasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true,
- dropAction:'move', treeView_Type: TreeViewType.outline,
+ dropAction:dropActionType.move, treeView_Type: TreeViewType.outline,
backgroundColor: "white", _xMargin: 0, _yMargin: 0, _createDocOnCR: true
}, funcs: {title: 'this.text?.Text'}},
];
@@ -307,6 +285,7 @@ export class CurrentUserUtils {
{ toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, clickFactory: DocCast(doc.emptyScript), funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a data viz node", title: "DataViz", icon: "chart-bar", dragFactory: doc.emptyDataViz as Doc, clickFactory: DocCast(doc.emptyDataViz)},
{ toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "file", dragFactory: doc.emptySlide as Doc, clickFactory: DocCast(doc.emptySlide), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
+ { toolTip: "Tap or drag to create a view slide", title: "View Slide", icon: "address-card", dragFactory: doc.emptyViewSlide as Doc,clickFactory: DocCast(doc.emptyViewSlide),openFactoryLocation: OpenWhere.overlay,funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize",dragFactory: doc.emptyHeader as Doc,clickFactory: DocCast(doc.emptyHeader), openFactoryAsDelegate: true, funcs: { hidden: "IsNoviceMode()"} },
{ toolTip: "Toggle a Calculator REPL", title: "replviewer", icon: "calculator", clickFactory: '<ScriptingRepl />' as any, openFactoryLocation: OpenWhere.overlay}, // hack: clickFactory is not a Doc but will get interpreted as a custom UI by the openDoc() onClick script
// { toolTip: "Toggle an UndoStack", title: "undostacker", icon: "calculator", clickFactory: "<UndoStack />" as any, openFactoryLocation: OpenWhere.overlay},
@@ -330,9 +309,9 @@ export class CurrentUserUtils {
});
const reqdOpts:DocumentOptions = {
- title: "Basic Item Creators", _layout_showTitle: "title", _xMargin: 0, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _chromeHidden: true, isSystem: true,
+ title: "Document Creators", _layout_showTitle: "title", _xMargin: 0, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _chromeHidden: true, isSystem: true,
_layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
- childDragAction: 'embed'
+ childDragAction: dropActionType.embed
};
const reqdScripts = { dropConverter: "convertToButtons(dragData)" };
return DocUtils.AssignScripts(DocUtils.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
@@ -374,7 +353,7 @@ export class CurrentUserUtils {
});
const reqdStackOpts:DocumentOptions ={
- title: "menuItemPanel", childDragAction: "same", layout_boxShadow: "rgba(0,0,0,0)", dontRegisterView: true, ignoreClick: true,
+ title: "menuItemPanel", childDragAction: dropActionType.same, layout_boxShadow: "rgba(0,0,0,0)", dontRegisterView: true, ignoreClick: true,
_chromeHidden: true, _gridGap: 0, _yMargin: 0, _xMargin: 0, _layout_autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, isSystem: true,
};
return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
@@ -457,21 +436,24 @@ export class CurrentUserUtils {
/// Search option on the left side button panel
static setupSearcher(doc: Doc, field:string) {
return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), {
- dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", isSystem: true, childDragAction: "embed",
+ dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", isSystem: true, childDragAction: dropActionType.embed,
_lockedPosition: true, _type_collection: CollectionViewType.Schema });
}
/// Initializes the panel of draggable tools that is opened from the left sidebar.
static setupToolsBtnPanel(doc: Doc, field:string) {
const myTools = DocCast(doc[field]);
- const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc, DocListCast(myTools?.data)?.length ? DocListCast(myTools.data)[0]:undefined);
- const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc,DocListCast(myTools?.data)?.length > 1 ? DocListCast(myTools.data)[1]:undefined);
+ const allTools = DocListCast(myTools?.data);
+ const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc, allTools?.length ? allTools[0]:undefined);
+ const userTools = allTools && allTools?.length > 1 ? allTools[1]:undefined;
+ const userBtns = CurrentUserUtils.setupUserDocumentCreatorButtons(doc, userTools);
+ //doc.myUserBtns = new PrefetchProxy(userBtns);
const reqdToolOps:DocumentOptions = {
title: "My Tools", isSystem: true, ignoreClick: true, layout_boxShadow: "0 0",
layout_explainer: "This is a palette of documents that can be created.",
_layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _chromeHidden: true,
};
- return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]);
+ return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, userBtns]);
}
/// initializes the left sidebar dashboard pane
@@ -494,8 +476,8 @@ export class CurrentUserUtils {
const childContextMenuIcons = ["tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
const reqdOpts:DocumentOptions = {
title: "My Dashboards", childHideLinkButton: true, treeView_FreezeChildren: "remove|add", treeView_HideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
- dropAction: "inSame", treeView_Type: TreeViewType.fileSystem, isFolder: true, isSystem: true, treeView_TruncateTitleWidth: 350, ignoreClick: true,
- layout_headerButton: newDashboardButton, childDragAction: "inSame",
+ dropAction: dropActionType.inPlace, treeView_Type: TreeViewType.fileSystem, isFolder: true, isSystem: true, treeView_TruncateTitleWidth: 350, ignoreClick: true,
+ layout_headerButton: newDashboardButton, childDragAction: dropActionType.inPlace,
_layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
contextMenuLabels:new List<string>(contextMenuLabels),
contextMenuIcons:new List<string>(contextMenuIcons),
@@ -520,36 +502,28 @@ export class CurrentUserUtils {
static setupFilesystem(doc: Doc, field:string) {
var myFilesystem = DocCast(doc[field]);
- const newFolder = `TreeView_addNewFolder()`;
const newFolderOpts: DocumentOptions = {
- _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _width: 30, _height: 30, undoIgnoreFields:new List<string>(['treeView_SortCriterion']),
+ _forceActive: true, _dragOnlyWithinContainer: true, _embedContainer: Doc.MyFilesystem, _layout_hideContextMenu: true, _width: 30, _height: 30, undoIgnoreFields:new List<string>(['treeView_SortCriterion']),
title: "New folder", color: Colors.BLACK, btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", isSystem: true
};
- const newFolderScript = { onClick: newFolder};
+ const newFolderScript = { onClick: CollectionTreeView.AddTreeFunc};
const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.layout_headerButton), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _height: 100, _forceActive: true,
- title: "My Documents", layout_headerButton: newFolderButton, treeView_HideTitle: true, dropAction: 'add', isSystem: true,
+ title: "My Documents", layout_headerButton: newFolderButton, treeView_HideTitle: true, dropAction: dropActionType.add, isSystem: true,
isFolder: true, treeView_Type: TreeViewType.fileSystem, childHideLinkButton: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
- treeView_TruncateTitleWidth: 350, ignoreClick: true, childDragAction: "embed",
- childContextMenuLabels: new List<string>(["Create new folder"]),
- childContextMenuIcons: new List<string>(["plus"]),
+ treeView_TruncateTitleWidth: 350, ignoreClick: true, childDragAction: dropActionType.embed,
layout_explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
};
const fileFolders = new Set(DocListCast(DocCast(doc[field])?.data));
- myFilesystem = DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, Array.from(fileFolders));
- const childContextMenuScripts = [newFolder];
- if (Cast(myFilesystem.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) {
- myFilesystem.childContextMenuScripts = new List<ScriptField>(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
- }
- return myFilesystem;
+ return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, Array.from(fileFolders));
}
/// initializes the panel displaying docs that have been recently closed
static setupRecentlyClosed(doc: Doc, field:string) {
const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, isFolder: true,
- title: "My Recently Closed", childHideLinkButton: true, treeView_HideTitle: true, childDragAction: "move", isSystem: true,
- treeView_TruncateTitleWidth: 350, ignoreClick: true, layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same",
+ title: "My Recently Closed", childHideLinkButton: true, treeView_HideTitle: true, childDragAction: dropActionType.move, isSystem: true,
+ treeView_TruncateTitleWidth: 350, ignoreClick: true, layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: dropActionType.same,
contextMenuLabels: new List<string>(["Empty recently closed"]),
contextMenuIcons:new List<string>(["trash"]),
layout_explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
@@ -562,8 +536,8 @@ export class CurrentUserUtils {
toolTip: "Empty recently closed",};
DocUtils.AssignDocField(recentlyClosed, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("this.target")});
- if (!Cast(recentlyClosed.contextMenuScripts, listSpec(ScriptField),null)?.find((script) => script?.script.originalScript === clearAll("self"))) {
- recentlyClosed.contextMenuScripts = new List<ScriptField>([ScriptField.MakeScript(clearAll("self"))!])
+ if (!Cast(recentlyClosed.contextMenuScripts, listSpec(ScriptField),null)?.find((script) => script?.script.originalScript === clearAll("this"))) {
+ recentlyClosed.contextMenuScripts = new List<ScriptField>([ScriptField.MakeScript(clearAll("this"))!])
}
return recentlyClosed;
}
@@ -572,7 +546,7 @@ export class CurrentUserUtils {
static setupUserDocView(doc: Doc, field:string) {
const reqdOpts:DocumentOptions = {
_lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view",
- layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same", ignoreClick: true, isSystem: true,
+ layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: dropActionType.same, ignoreClick: true, isSystem: true,
treeView_HideTitle: true, treeView_TruncateTitleWidth: 350
};
if (!doc[field]) DocUtils.AssignOpts(doc, {treeView_Open: true, treeView_ExpandedView: "fields" });
@@ -604,16 +578,16 @@ export class CurrentUserUtils {
CurrentUserUtils.createToolButton(opts), scripts, funcs);
const btnDescs = [// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet
- { scripts: { onClick: "undo()"}, opts: { title: "Undo", icon: "undo-alt", toolTip: "Undo ⌘Z" }},
- { scripts: { onClick: "redo()"}, opts: { title: "Redo", icon: "redo-alt", toolTip: "Redo ⌘⇧Z" }},
- { scripts: { }, opts: { title: "undoStack", layout: "<UndoStack>", toolTip: "Undo/Redo Stack"}}, // note: layout fields are hacks -- they don't actually run through the JSX parser (yet)
- { scripts: { }, opts: { title: "linker", layout: "<LinkingUI>", toolTip: "link started"}},
- { scripts: { }, opts: { title: "currently playing", layout: "<CurrentlyPlayingUI>", toolTip: "currently playing media"}},
- { scripts: { }, opts: { title: "Branching", layout: "<Branching>", toolTip: "Branch, baby!"}}
+ { scripts: { onClick: "undo()"}, opts: { title: "Undo", icon: "undo-alt", toolTip: "Undo ⌘Z" }},
+ { scripts: { onClick: "redo()"}, opts: { title: "Redo", icon: "redo-alt", toolTip: "Redo ⌘⇧Z" }},
+ { scripts: { }, opts: { title: "undoStack", layout: "<UndoStack>", toolTip: "Undo/Redo Stack"}}, // note: layout fields are hacks -- they don't actually run through the JSX parser (yet)
+ { scripts: { }, opts: { title: "linker", layout: "<LinkingUI>", toolTip: "link started"}},
+ { scripts: { }, opts: { title: "currently playing", layout: "<CurrentlyPlayingUI>", toolTip: "currently playing media"}},
+ { scripts: { }, opts: { title: "Branching", layout: "<Branching>", toolTip: "Branch, baby!"}}
];
const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, defaultDoubleClick: 'ignore', undoIgnoreFields: new List<string>(['opacity']), _dragOnlyWithinContainer: true, ...desc.opts}, desc.scripts));
const dockBtnsReqdOpts:DocumentOptions = {
- title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: 'move',
+ title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: dropActionType.move,
childDontRegisterViews: true, linearView_IsOpen: true, linearView_Expandable: true, ignoreClick: true
};
reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "Redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
@@ -637,9 +611,7 @@ export class CurrentUserUtils {
return [
{ title: "Snap", icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"snaplines", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
{ title: "Grid", icon: "border-all", toolTip: "Show Grid", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"grid", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
- { title: "View All", icon: "object-group", toolTip: "Keep all Docs in View",btnType: ButtonType.ToggleButton, ignoreClick:true, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
- // want the same style as toggle button, but don't want it to act as an actual toggle, so set disableToggle to true,
- { title: "Fit All", icon: "arrows-left-right", toolTip: "Fit Docs to View (once)",btnType: ButtonType.ClickButton,ignoreClick:false,expertMode: false, toolType:"fitOnce", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+ { title: "Fit All", icon: "object-group", toolTip: "Fit Docs to View (double click to make sticky)",btnType: ButtonType.ToggleButton, ignoreClick:true, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}', onDoubleClick: '{ return showFreeform(this.toolType, _readOnly_, true);}'}}, // Only when floating document is selected in freeform
{ title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
{ title: "Cards", icon: "brain", toolTip: "Flashcards", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"flashcards", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
{ title: "Arrange", icon:"arrow-down-short-wide",toolTip:"Toggle Auto Arrange", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"arrange", funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
@@ -769,7 +741,7 @@ export class CurrentUserUtils {
/// Initializes all the default buttons for the top bar context menu
static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
- const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", undoIgnoreFields:new List<string>(['width', "linearView_IsOpen"]), flexGap: 0, childDragAction: 'embed', childDontRegisterViews: true, linearView_IsOpen: true, ignoreClick: true, linearView_Expandable: false, _height: 35 };
+ const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", undoIgnoreFields:new List<string>(['width', "linearView_IsOpen"]), flexGap: 0, childDragAction: dropActionType.embed, childDontRegisterViews: true, linearView_IsOpen: true, ignoreClick: true, linearView_Expandable: false, _height: 35 };
const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => this.setupContextMenuBtn(params, ctxtMenuBtnsDoc) );
return DocUtils.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns);
@@ -795,7 +767,7 @@ export class CurrentUserUtils {
];
const btns = btnDescs.map(desc => dockBtn({_width: desc.opts.width??30, _height: 30, defaultDoubleClick: 'ignore', undoIgnoreFields: new List<string>(['opacity']), _dragOnlyWithinContainer: true, ...desc.opts}, desc.scripts, desc.funcs));
const dockBtnsReqdOpts:DocumentOptions = {
- title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: 'move',
+ title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: dropActionType.move,
childDontRegisterViews: true, linearView_IsOpen: true, linearView_Expandable: false, ignoreClick: true
};
return DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns);
@@ -823,7 +795,7 @@ export class CurrentUserUtils {
// When the user views one of these documents, it will be added to the sharing documents 'viewed' list field
// The sharing document also stores the user's color value which helps distinguish shared documents from personal documents
static setupSharedDocs(doc: Doc, sharingDocumentId: string) {
- const dblClkScript = "{scriptContext.openLevel(documentView); addDocToList(documentView.props.treeViewDoc, 'viewed', documentView.Document);}";
+ const dblClkScript = "{scriptContext.openLevel(documentView); addDocToList(getSharingDoc(), 'viewed', documentView.Document);}";
const sharedScripts = { treeView_ChildDoubleClick: dblClkScript, }
const sharedDocOpts:DocumentOptions = {
@@ -835,7 +807,7 @@ export class CurrentUserUtils {
// childContextMenuLabels: new List<string>(["Add to Dashboards",]),
// childContextMenuIcons: new List<string>(["user-plus",]),
"acl-Guest": SharingPermissions.Augment, "_acl-Guest": SharingPermissions.Augment,
- childDragAction: "embed", isSystem: true, contentPointerEvents: "none", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
+ childDragAction: dropActionType.embed, isSystem: true, childContentPointerEvents: "none", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
// NOTE: treeView_HideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar
_layout_showTitle: "title", treeView_HideTitle: true, ignoreClick: true, _lockedPosition: true, layout_boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true,
layout_explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
@@ -850,7 +822,7 @@ export class CurrentUserUtils {
const reqdOpts:DocumentOptions = {
title: "My Imports", _forceActive: true, _layout_showTitle: "title", childLayoutString: ImportElementBox.LayoutString('data'),
_dragOnlyWithinContainer: true, _layout_hideContextMenu: true, childLimitHeight: 0, onClickScriptDisable:"never",
- childDragAction: "copy", _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, layout_boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true,
+ childDragAction: dropActionType.copy, _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, layout_boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true,
dontRegisterView: true, layout_explainer: "This is where documents that are Imported into Dash will go."
};
const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.MasonryDocument([], opts), reqdOpts, undefined, {onClick: "deselectAll()"});
@@ -912,7 +884,7 @@ export class CurrentUserUtils {
this.setupDocTemplates(doc); // sets up the template menu of templates
//this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) => Docs.Prototypes.MainScriptDocument(), {});
- DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "My Header Bar", isSystem: true, _chromeHidden:true, childLayoutFitWidth:false, childDocumentsActive:false, dropAction: 'move'}); // drop down panel at top of dashboard for stashing documents
+ DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "My Header Bar", isSystem: true, _chromeHidden:true, layout_maxShown: 10, childLayoutFitWidth:false, childDocumentsActive:false, dropAction: dropActionType.move}); // drop down panel at top of dashboard for stashing documents
Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards)
Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs)
@@ -935,9 +907,9 @@ export class CurrentUserUtils {
const options = pair[1] as FInfo;
const opts:DocumentOptions = { isSystem: true, title: pair[0], ...OmitKeys(options, ["values"]).omit, fieldIsLayout: pair[0].startsWith("_")};
switch (options.fieldType) {
- case "boolean": opts.fieldValues = new List<boolean>(options.values as any); break;
- case "number": opts.fieldValues = new List<number>(options.values as any); break;
- case Doc.name: opts.fieldValues = new List<Doc>(options.values as any); break;
+ case FInfoFieldType.boolean: opts.fieldValues = new List<boolean>(options.values as any); break;
+ case FInfoFieldType.number: opts.fieldValues = new List<number>(options.values as any); break;
+ case FInfoFieldType.Doc: opts.fieldValues = new List<Doc>(options.values as any); break;
default: opts.fieldValues = new List<string>(options.values as any); break;// string, pointerEvents, dimUnit, dropActionType
}
DocUtils.AssignDocField(infos, pair[0], opts => Doc.assign(new Doc(), OmitKeys(opts,["values"]).omit), opts);
@@ -1029,4 +1001,5 @@ ScriptingGlobals.add(function IsExploreMode() { return SnappingManager.ExploreMo
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");
ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar");
-ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); \ No newline at end of file
+ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; });
+ScriptingGlobals.add(function getSharingDoc() {return Doc.SharingDoc() }); \ No newline at end of file
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 7fef5cc79..a38a330da 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,6 +1,7 @@
+import { Howl } from 'howler';
import { action, computed, makeObservable, observable, ObservableSet, observe } from 'mobx';
-import { Doc, DocListCast, Opt } from '../../fields/Doc';
-import { AclAdmin, AclEdit, Animation } from '../../fields/DocSymbols';
+import { Doc, Opt } from '../../fields/Doc';
+import { AclAdmin, AclEdit, Animation, DocData } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
import { listSpec } from '../../fields/Schema';
import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
@@ -10,13 +11,13 @@ import { CollectionViewType } from '../documents/DocumentTypes';
import { CollectionDockingView } from '../views/collections/CollectionDockingView';
import { TabDocView } from '../views/collections/TabDocView';
import { LightboxView } from '../views/LightboxView';
-import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView';
+import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView';
+import { FocusViewOptions } from '../views/nodes/FieldView';
import { KeyValueBox } from '../views/nodes/KeyValueBox';
import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox';
import { PresBox } from '../views/nodes/trails';
import { ScriptingGlobals } from './ScriptingGlobals';
import { SelectionManager } from './SelectionManager';
-import { Howl } from 'howler';
export class DocumentManager {
private static _instance: DocumentManager;
@@ -27,10 +28,8 @@ export class DocumentManager {
//global holds all of the nodes (regardless of which collection they're in)
@observable _documentViews = new Set<DocumentView>();
@observable.shallow public CurrentlyLoading: Doc[] = [];
- @observable.shallow public LinkAnchorBoxViews: DocumentView[] = [];
- @observable.shallow public LinkedDocumentViews: { a: DocumentView; b: DocumentView; l: Doc }[] = [];
@computed public get DocumentViews() {
- return Array.from(this._documentViews).filter(view => !(view.ComponentView instanceof KeyValueBox) && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(view.docViewPath)));
+ return Array.from(this._documentViews).filter(view => !(view.ComponentView instanceof KeyValueBox) && (!LightboxView.LightboxDoc || LightboxView.Contains(view)));
}
public AddDocumentView(dv: DocumentView) {
this._documentViews.add(dv);
@@ -89,37 +88,14 @@ export class DocumentManager {
@action
public AddView = (view: DocumentView) => {
- if (view._props.LayoutTemplateString?.includes(KeyValueBox.name)) return;
- if (view._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
- const viewAnchorIndex = view._props.LayoutTemplateString.includes('link_anchor_2') ? 'link_anchor_2' : 'link_anchor_1';
- const link = view.Document;
- this.LinkAnchorBoxViews?.filter(dv => Doc.AreProtosEqual(dv.Document, link) && !dv._props.LayoutTemplateString?.includes(viewAnchorIndex)).forEach(otherView =>
- this.LinkedDocumentViews.push({
- a: viewAnchorIndex === 'link_anchor_2' ? otherView : view,
- b: viewAnchorIndex === 'link_anchor_2' ? view : otherView,
- l: link,
- })
- );
- this.LinkAnchorBoxViews.push(view);
- } else {
+ if (!view._props.LayoutTemplateString?.includes(KeyValueBox.name) &&
+ !view._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
this.AddDocumentView(view);
- }
- this.callAddViewFuncs(view);
+ this.callAddViewFuncs(view);
+ } // prettier-ignore
};
public RemoveView = action((view: DocumentView) => {
- this.LinkedDocumentViews.slice().forEach(
- action(pair => {
- if (pair.a === view || pair.b === view) {
- const li = this.LinkedDocumentViews.indexOf(pair);
- li !== -1 && this.LinkedDocumentViews.splice(li, 1);
- }
- })
- );
-
- if (view._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
- const index = this.LinkAnchorBoxViews.indexOf(view);
- this.LinkAnchorBoxViews.splice(index, 1);
- } else {
+ if (!view._props.LayoutTemplateString?.includes(KeyValueBox.name) && !view._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
this.DeleteDocumentView(view);
}
SelectionManager.DeselectView(view);
@@ -135,7 +111,7 @@ export class DocumentManager {
});
if (toReturn.length === 0) {
DocumentManager.Instance.DocumentViews.forEach(view => {
- if (Doc.GetProto(view.Document)?.[Id] === id) {
+ if (view.Document[DocData]?.[Id] === id) {
toReturn.push(view);
}
});
@@ -153,15 +129,15 @@ export class DocumentManager {
return passes.reduce(
(toReturn, pass) =>
toReturn ??
- docViewArray.filter(view => view.Document === target).find(view => !pass || view._props.docViewPath().lastElement() === preferredCollection) ??
- docViewArray.filter(view => Doc.AreProtosEqual(view.Document, target)).find(view => !pass || view._props.docViewPath().lastElement() === preferredCollection),
+ docViewArray.filter(view => view.Document === target).find(view => !pass || view.containerViewPath?.().lastElement() === preferredCollection) ??
+ docViewArray.filter(view => Doc.AreProtosEqual(view.Document, target)).find(view => !pass || view.containerViewPath?.().lastElement() === preferredCollection),
undefined as Opt<DocumentView>
);
}
public getLightboxDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
const views: DocumentView[] = [];
- DocumentManager.Instance.DocumentViews.forEach(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.Document, toFind) && views.push(view));
+ DocumentManager.Instance.DocumentViews.forEach(view => LightboxView.Contains(view) && Doc.AreProtosEqual(view.Document, toFind) && views.push(view));
return views?.find(view => view.ContentDiv?.getBoundingClientRect().width /*&& view._props.focus !== returnFalse) || views?.find(view => view._props.focus !== returnFalse*/) || (views.length ? views[0] : undefined);
};
public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
@@ -179,8 +155,8 @@ export class DocumentManager {
toFindIn;
const toReturn: DocumentView[] = [];
- const docViews = DocumentManager.Instance.DocumentViews.filter(view => !LightboxView.IsLightboxDocView(view.docViewPath));
- const lightViews = DocumentManager.Instance.DocumentViews.filter(view => LightboxView.IsLightboxDocView(view.docViewPath));
+ const docViews = DocumentManager.Instance.DocumentViews.filter(view => !LightboxView.Contains(view));
+ const lightViews = DocumentManager.Instance.DocumentViews.filter(view => LightboxView.Contains(view));
// heuristic to return the "best" documents first:
// choose a document in the lightbox first
@@ -224,7 +200,7 @@ export class DocumentManager {
}
public static removeOverlayViews() {
- DocumentManager._overlayViews?.forEach(action(view => (view.textHtmlOverlay = undefined)));
+ DocumentManager._overlayViews?.forEach(view => view.setTextHtmlOverlay(undefined, undefined));
DocumentManager._overlayViews?.clear();
}
static _overlayViews = new ObservableSet<DocumentView>();
@@ -233,11 +209,25 @@ export class DocumentManager {
finished?.();
};
+ public static LinkCommonAncestor(linkDoc: Doc) {
+ const anchor = (which: number) => {
+ const anch = DocCast(linkDoc['link_anchor_' + which]);
+ const anchor = anch?.layout_unrendered ? DocCast(anch.annotationOn) : anch;
+ return DocumentManager.Instance.getDocumentView(anchor);
+ };
+ const anchor1 = anchor(1);
+ const anchor2 = anchor(2);
+ return anchor1
+ ?.docViewPath()
+ .reverse()
+ .find(ancestor => anchor2?.docViewPath().includes(ancestor));
+ }
+
// shows a documentView by:
// traverses down through the viewPath of contexts to the view:
// focusing on each context
- public showDocumentView = async (targetDocView: DocumentView, options: DocFocusOptions) => {
- const docViewPath = targetDocView.docViewPath.slice();
+ public showDocumentView = async (targetDocView: DocumentView, options: FocusViewOptions) => {
+ const docViewPath = [...(targetDocView.containerViewPath?.() ?? []), targetDocView];
let rootContextView = docViewPath.shift();
await (rootContextView && this.focusViewsInPath(rootContextView, options, async () => ({ childDocView: docViewPath.shift(), viewSpec: undefined, focused: false })));
if (options.toggleTarget && (!options.didMove || targetDocView.Document.hidden)) targetDocView.Document.hidden = !targetDocView.Document.hidden;
@@ -252,12 +242,17 @@ export class DocumentManager {
// and finally restoring the targetDoc to the viewSpec specified by the last document which may either be the targetDoc, or a viewSpec that describes the targetDoc configuration
public showDocument = async (
targetDoc: Doc, // document to display
- options: DocFocusOptions, // options for how to navigate to target
+ options: FocusViewOptions, // options for how to navigate to target
finished?: (changed: boolean) => void // func called after focusing on target with flag indicating whether anything needed to be done.
) => {
Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, targetDoc);
const docContextPath = DocumentManager.GetContextPath(targetDoc, true);
if (docContextPath.some(doc => doc.hidden)) options.toggleTarget = false;
+ const tabView = Array.from(TabDocView._allTabs).find(view => view._document === docContextPath[0]);
+ if (!tabView?._activated && tabView?._document) {
+ options.toggleTarget = false;
+ TabDocView.Activate(tabView?._document);
+ }
let rootContextView =
docContextPath.length &&
(await new Promise<DocumentView>(res => {
@@ -274,7 +269,7 @@ export class DocumentManager {
// even if we found the document view, if the target is a lightbox, we try to open it in the lightbox to preserve lightbox semantics (eg, there's only one active doc in the lightbox)
const target = DocCast(targetDoc.annotationOn, targetDoc);
const contextView = this.getDocumentView(DocCast(target.embedContainer));
- if (contextView?.docView?._componentView?.addDocTab?.(target, OpenWhere.lightbox)) {
+ if (contextView?.ComponentView?.addDocTab?.(target, OpenWhere.lightbox)) {
await new Promise<void>(waitres => setTimeout(() => waitres()));
}
}
@@ -293,7 +288,7 @@ export class DocumentManager {
focusViewsInPath = async (
docView: DocumentView, //
- options: DocFocusOptions,
+ options: FocusViewOptions,
iterator: (docView: DocumentView) => Promise<{ viewSpec: Opt<Doc>; childDocView: Opt<DocumentView>; focused: boolean }>
) => {
let contextView: DocumentView | undefined; // view containing context that contains target
@@ -313,7 +308,7 @@ export class DocumentManager {
};
@action
- restoreDocView(viewSpec: Opt<Doc>, docView: DocumentView, options: DocFocusOptions, contextView: Opt<DocumentView>, targetDoc: Doc) {
+ restoreDocView(viewSpec: Opt<Doc>, docView: DocumentView, options: FocusViewOptions, contextView: Opt<DocumentView>, targetDoc: Doc) {
if (viewSpec && docView) {
//if (docView.ComponentView instanceof FormattedTextBox)
//viewSpec !== docView.Document &&
@@ -327,24 +322,21 @@ export class DocumentManager {
if (options.zoomTextSelections && Doc.UnhighlightTimer && contextView && targetDoc.text_html) {
// if the docView is a text anchor, the contextView is the PDF/Web/Text doc
- contextView.htmlOverlayEffect = options.effect;
- contextView.textHtmlOverlayTime = options.zoomTime;
- contextView.textHtmlOverlay = StrCast(targetDoc.text_html);
+ contextView.setTextHtmlOverlay(StrCast(targetDoc.text_html), options.effect);
DocumentManager._overlayViews.add(contextView);
}
Doc.AddUnHighlightWatcher(() => {
docView.Document[Animation] = undefined;
DocumentManager.removeOverlayViews();
- contextView && (contextView.htmlOverlayEffect = undefined);
});
}
}
}
-export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCentered: true, zoomScale: 0, openLocation: OpenWhere.toggleRight }, containingDoc?: Doc) {
+export function DocFocusOrOpen(doc: Doc, options: FocusViewOptions = { willZoomCentered: true, zoomScale: 0, openLocation: OpenWhere.toggleRight }, containingDoc?: Doc) {
const func = () => {
const cv = DocumentManager.Instance.getDocumentView(containingDoc);
const dv = DocumentManager.Instance.getDocumentView(doc, cv);
- if (dv && (!containingDoc || dv._props.docViewPath().lastElement()?.Document === containingDoc)) {
+ if (dv && (!containingDoc || dv.containerViewPath?.().lastElement()?.Document === containingDoc)) {
DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.Document));
} else {
const container = DocCast(containingDoc ?? doc.embedContainer ?? Doc.BestEmbedding(doc));
@@ -357,8 +349,8 @@ export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCe
});
}
};
- if (Doc.IsDataProto(doc) && DocListCast(doc.proto_embeddings).some(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))) {
- doc = DocListCast(doc.proto_embeddings).find(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))!;
+ if (Doc.IsDataProto(doc) && Doc.GetEmbeddings(doc).some(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))) {
+ doc = Doc.GetEmbeddings(doc).find(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))!;
}
if (doc.hidden) {
doc.hidden = false;
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 9ede18ed5..aa0f77c72 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,10 +1,24 @@
+/**
+ * The DragManager handles all dragging interactions that occur entirely within Dash (as opposed to external drag operations from the file system, etc)
+ *
+ * Events are generated for
+ * a pause in the drag movement (dashDragMovePause) as a Doc(s) is dragged,
+ * just before (dashPreDrop) a Doc(s) is dropped,
+ * and just after (dashDropEvent) a Doc(s) is dropped
+ * If the document is dragged and paused over the golden layout header tabs, the
+ * drag interaction will switch to a golden layout tab drag.
+ *
+ * All drag operations can be aborted by hitting the Esc key
+ *
+ */
+
import { action, observable, runInAction } from 'mobx';
import { DateField } from '../../fields/DateField';
import { Doc, Field, Opt, StrListCast } from '../../fields/Doc';
import { List } from '../../fields/List';
import { PrefetchProxy } from '../../fields/Proxy';
import { ScriptField } from '../../fields/ScriptField';
-import { ScriptCast, StrCast } from '../../fields/Types';
+import { ScriptCast } from '../../fields/Types';
import { emptyFunction, Utils } from '../../Utils';
import { Docs, DocUtils } from '../documents/Documents';
import { CollectionFreeFormDocumentView } from '../views/nodes/CollectionFreeFormDocumentView';
@@ -13,9 +27,18 @@ import { ScriptingGlobals } from './ScriptingGlobals';
import { SelectionManager } from './SelectionManager';
import { SnappingManager } from './SnappingManager';
import { UndoManager } from './UndoManager';
+import { DocData } from '../../fields/DocSymbols';
const { default : { contextMenuZindex } } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore
-export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'inSame' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
+export enum dropActionType {
+ embed = 'embed', // create a new embedding of the dragged document for the new location
+ copy = 'copy', // copy the dragged document
+ move = 'move', // move the dragged document to the drop location after removing it from where it was
+ add = 'add', // add the dragged document to the drop location without removing it from where it was
+ same = 'same', // only allow drop within same collection (or same hierarchical tree collection)
+ inPlace = 'inSame', // keep document in place (unless overridden by a drag modifier)
+ proto = 'proto',
+} // undefined = move, same = move but doesn't call dropPropertiesToRemove
/**
* Initialize drag
@@ -78,8 +101,6 @@ export namespace DragManager {
export interface DragOptions {
dragComplete?: (e: DragCompleteEvent) => void; // function to invoke when drag has completed
hideSource?: boolean; // hide source document during drag
- offsetX?: number; // offset of top left of source drag visual from cursor
- offsetY?: number;
noAutoscroll?: boolean;
}
@@ -128,9 +149,9 @@ export namespace DragManager {
treeViewDoc?: Doc;
offset: number[];
canEmbed?: boolean;
- userDropAction: dropActionType; // the user requested drop action -- this will be honored as specified by modifier keys
+ userDropAction?: dropActionType; // the user requested drop action -- this will be honored as specified by modifier keys
defaultDropAction?: dropActionType; // an optionally specified default drop action when there is no user drop actionl - this will be honored if there is no user drop action
- dropAction: dropActionType; // a drop action request by the initiating code. the actual drop action may be different -- eg, if the request is 'embed', but the document is dropped within the same collection, the drop action will be switched to 'move'
+ dropAction?: dropActionType; // a drop action request by the initiating code. the actual drop action may be different -- eg, if the request is 'embed', but the document is dropped within the same collection, the drop action will be switched to 'move'
dropPropertiesToRemove?: string[];
moveDocument?: MoveFunction;
removeDocument?: RemoveFunction;
@@ -169,8 +190,8 @@ export namespace DragManager {
dropDocCreator: (annotationOn: Doc | undefined) => Doc;
dropDocument?: Doc;
offset: number[];
- dropAction: dropActionType;
- userDropAction: dropActionType;
+ dropAction?: dropActionType;
+ userDropAction?: dropActionType;
}
let defaultPreDropFunc = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
@@ -180,7 +201,7 @@ export namespace DragManager {
}
};
- export function MakeDropTarget(element: HTMLElement, dropFunc: (e: Event, de: DropEvent) => void, doc?: Doc, preDropFunc?: (e: Event, de: DropEvent, targetAction: dropActionType) => void): DragDropDisposer {
+ export function MakeDropTarget(element: HTMLElement, dropFunc: (e: Event, de: DropEvent) => void, doc: Doc, preDropFunc?: (e: Event, de: DropEvent, targetAction: dropActionType) => void): DragDropDisposer {
if ('canDrop' in element.dataset) {
throw new Error("Element is already droppable, can't make it droppable again");
}
@@ -188,13 +209,13 @@ export namespace DragManager {
const handler = (e: Event) => dropFunc(e, (e as CustomEvent<DropEvent>).detail);
const preDropHandler = (e: Event) => {
const de = (e as CustomEvent<DropEvent>).detail;
- (preDropFunc ?? defaultPreDropFunc)(e, de, StrCast(doc?.dropAction) as dropActionType);
+ (preDropFunc ?? defaultPreDropFunc)(e, de, doc.dropAction as any as dropActionType);
};
element.addEventListener('dashOnDrop', handler);
- doc && element.addEventListener('dashPreDrop', preDropHandler);
+ element.addEventListener('dashPreDrop', preDropHandler);
return () => {
element.removeEventListener('dashOnDrop', handler);
- doc && element.removeEventListener('dashPreDrop', preDropHandler);
+ element.removeEventListener('dashPreDrop', preDropHandler);
delete element.dataset.canDrop;
};
}
@@ -217,19 +238,19 @@ export namespace DragManager {
dragData.draggedDocuments.map(async d =>
!dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
- : docDragData.dropAction === 'embed'
+ : docDragData.dropAction === dropActionType.embed
? Doc.BestEmbedding(d)
- : docDragData.dropAction === 'add'
+ : docDragData.dropAction === dropActionType.add
? d
- : docDragData.dropAction === 'proto'
- ? Doc.GetProto(d)
- : docDragData.dropAction === 'copy'
+ : docDragData.dropAction === dropActionType.proto
+ ? d[DocData]
+ : docDragData.dropAction === dropActionType.copy
? (await Doc.MakeClone(d)).clone
: d
)
)
).filter(d => d);
- !['same', 'proto'].includes(docDragData.dropAction as any) &&
+ ![dropActionType.same, dropActionType.proto].includes(docDragData.dropAction as any) &&
docDragData.droppedDocuments
// .filter(drop => !drop.dragOnlyWithinContainer || ['embed', 'copy'].includes(docDragData.dropAction as any))
.forEach((drop: Doc, i: number) => {
@@ -250,9 +271,9 @@ export namespace DragManager {
export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
const finishDrag = (e: DragCompleteEvent) => {
const bd = Docs.Create.ButtonDocument({ toolTip: title, z: 1, _width: 150, _height: 50, title, onClick: ScriptField.MakeScript(script) });
- params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc))); // copy all "captured" arguments into document parameterfields
+ params.map(p => Object.keys(vars).indexOf(p) !== -1 && (bd[DocData][p] = new PrefetchProxy(vars[p] as Doc))); // copy all "captured" arguments into document parameterfields
initialize?.(bd);
- Doc.GetProto(bd)['onClick-paramFieldKeys'] = new List<string>(params);
+ bd[DocData]['onClick-paramFieldKeys'] = new List<string>(params);
e.docDragData && (e.docDragData.droppedDocuments = [bd]);
return e;
};
@@ -503,9 +524,9 @@ export namespace DragManager {
const moveHandler = (e: PointerEvent) => {
e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop
if (dragData instanceof DocumentDragData) {
- dragData.userDropAction = e.ctrlKey && e.altKey ? 'copy' : e.ctrlKey ? 'embed' : dragData.defaultDropAction;
+ dragData.userDropAction = e.ctrlKey && e.altKey ? dropActionType.copy : e.shiftKey ? dropActionType.move : e.ctrlKey ? dropActionType.embed : dragData.defaultDropAction;
}
- if (((e.target as any)?.className === 'lm_tabs' || (e.target as any)?.className === 'lm_header') && dragData.draggedDocuments.length === 1) {
+ if (['lm_tab', 'lm_title_wrap', 'lm_tabs', 'lm_header'].includes(typeof (e.target as any).className === 'string' ? (e.target as any)?.className : '') && dragData.draggedDocuments.length === 1) {
if (!startWindowDragTimer) {
startWindowDragTimer = setTimeout(async () => {
startWindowDragTimer = undefined;
@@ -529,7 +550,7 @@ export namespace DragManager {
if (target && !Doc.UserDoc()._noAutoscroll && !options?.noAutoscroll && !dragData.draggedDocuments?.some((d: any) => d._freeform_noAutoPan)) {
const autoScrollHandler = () => {
target.dispatchEvent(
- new CustomEvent<React.DragEvent>('dashDragAutoScroll', {
+ new CustomEvent<React.DragEvent>('dashDragMovePause', {
bubbles: true,
detail: {
shiftKey: e.shiftKey,
@@ -551,7 +572,7 @@ export namespace DragManager {
screenY: e.screenY,
detail: e.detail,
view: e.view ? e.view : (new Window() as any),
- nativeEvent: new DragEvent('dashDragAutoScroll'),
+ nativeEvent: new DragEvent('dashDragMovePause'),
currentTarget: target,
target: target,
bubbles: true,
@@ -565,7 +586,7 @@ export namespace DragManager {
isPropagationStopped: () => ('not implemented for this event' ? false : false),
persist: emptyFunction,
timeStamp: e.timeStamp,
- type: 'dashDragAutoScroll',
+ type: 'dashDragMovePause',
},
})
);
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 2c371f28e..54066d267 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -1,8 +1,9 @@
import { Doc, DocListCast, Opt } from '../../fields/Doc';
+import { DocData } from '../../fields/DocSymbols';
import { ObjectField } from '../../fields/ObjectField';
import { RichTextField } from '../../fields/RichTextField';
import { listSpec } from '../../fields/Schema';
-import { ScriptField } from '../../fields/ScriptField';
+import { ComputedField, ScriptField } from '../../fields/ScriptField';
import { Cast, StrCast } from '../../fields/Types';
import { ImageField } from '../../fields/URLField';
import { Docs } from '../documents/Documents';
@@ -12,7 +13,7 @@ import { DragManager } from './DragManager';
import { ScriptingGlobals } from './ScriptingGlobals';
export function MakeTemplate(doc: Doc, first: boolean = true, rename: Opt<string> = undefined, templateField: string = '') {
- if (templateField) Doc.GetProto(doc).title = templateField; /// the title determines which field is being templated
+ if (templateField) doc[DocData].title = templateField; /// the title determines which field is being templated
doc.isTemplateDoc = makeTemplate(doc, first, rename);
return doc;
}
@@ -33,20 +34,17 @@ function makeTemplate(doc: Doc, first: boolean = true, rename: Opt<string> = und
let any = false;
docs.forEach(d => {
if (!StrCast(d.title).startsWith('-')) {
- any = Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc)) || any;
+ any = Doc.MakeMetadataFieldTemplate(d, layoutDoc[DocData]) || any;
} else if (d.type === DocumentType.COL || d.data instanceof RichTextField) {
any = makeTemplate(d, false) || any;
}
});
- if (first) {
- if (!docs.length) {
- // bcz: feels hacky : if the root level document has items, it's not a field template
- any = Doc.MakeMetadataFieldTemplate(doc, Doc.GetProto(layoutDoc)) || any;
- }
- }
- if (layoutDoc[fieldKey] instanceof RichTextField || layoutDoc[fieldKey] instanceof ImageField) {
+ if (first && !docs.length) {
+ // bcz: feels hacky : if the root level document has items, it's not a field template
+ any = Doc.MakeMetadataFieldTemplate(doc, layoutDoc[DocData], true) || any;
+ } else if (layoutDoc[fieldKey] instanceof RichTextField || layoutDoc[fieldKey] instanceof ImageField) {
if (!StrCast(layoutDoc.title).startsWith('-')) {
- any = Doc.MakeMetadataFieldTemplate(layoutDoc, Doc.GetProto(layoutDoc));
+ any = Doc.MakeMetadataFieldTemplate(layoutDoc, layoutDoc[DocData], true);
}
}
rename && (doc.title = rename);
@@ -75,11 +73,14 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
_nativeHeight: 100,
_width: 100,
_height: 100,
+ _layout_hideContextMenu: true,
backgroundColor: StrCast(doc.backgroundColor),
title: StrCast(layoutDoc.title),
btnType: ButtonType.ClickButton,
icon: 'bolt',
+ isSystem: false,
});
+ dbox.title = ComputedField.MakeFunction('this.dragFactory.title');
dbox.dragFactory = layoutDoc;
dbox.dropPropertiesToRemove = doc.dropPropertiesToRemove instanceof ObjectField ? ObjectField.MakeCopy(doc.dropPropertiesToRemove) : undefined;
dbox.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory)');
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index f46c2d431..c5f307f44 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -30,15 +30,15 @@ export namespace Hypothesis {
if (currentDoc && Cast(currentDoc.data, WebField)?.url.href === uri) return currentDoc; // always check first whether the currently selected doc is the annotation's source, only use Search otherwise
const results: Doc[] = [];
- await SearchUtil.Search('web', true).then(
- action(async (res: SearchUtil.DocSearchResult) => {
- const docs = res.docs;
- const filteredDocs = docs.filter(doc => doc.author === Doc.CurrentUserEmail && doc.type === DocumentType.WEB && doc.data);
- filteredDocs.forEach(doc => {
- uri === Cast(doc.data, WebField)?.url.href && results.push(doc); // TODO check visited sites history?
- });
- })
- );
+ // await SearchUtil.Search('web', true).then(
+ // action(async (res: SearchUtil.DocSearchResult) => {
+ // const docs = res.docs;
+ // const filteredDocs = docs.filter(doc => doc.author === Doc.CurrentUserEmail && doc.type === DocumentType.WEB && doc.data);
+ // filteredDocs.forEach(doc => {
+ // uri === Cast(doc.data, WebField)?.url.href && results.push(doc); // TODO check visited sites history?
+ // });
+ // })
+ // );
const onScreenResults = results.filter(doc => DocumentManager.Instance.getFirstDocumentView(doc));
return onScreenResults.length ? onScreenResults[0] : results.length ? results[0] : undefined; // prioritize results that are currently on the screen
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index b6dbea33a..398ba3c04 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -156,7 +156,7 @@
// x: NumCast(doc.x),
// y: NumCast(doc.y) + offset,
// };
-// const parent = this.props.DocumentView?.()._props.docViewPath().lastElement();
+// const parent = this.props.DocumentView?.().containerViewPath().lastElement();
// if (parent?.Document.type === DocumentType.COL) {
// let importContainer: Doc;
// if (docs.length < 50) {
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 2df4d1ca8..20261859c 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -1,16 +1,17 @@
-import { action, observable, runInAction } from 'mobx';
+import { action, runInAction } from 'mobx';
import { Doc, DocListCast, Field, FieldResult, Opt } from '../../fields/Doc';
import { ScriptField } from '../../fields/ScriptField';
import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../fields/Types';
import { DocumentType } from '../documents/DocumentTypes';
-import { DocFocusOptions, OpenWhere } from '../views/nodes/DocumentView';
+import { OpenWhere } from '../views/nodes/DocumentView';
+import { FocusViewOptions } from '../views/nodes/FieldView';
import { PresBox } from '../views/nodes/trails';
import { DocumentManager } from './DocumentManager';
import { LinkManager } from './LinkManager';
import { ScriptingGlobals } from './ScriptingGlobals';
import { SelectionManager } from './SelectionManager';
-import { UndoManager } from './UndoManager';
import { SnappingManager } from './SnappingManager';
+import { UndoManager } from './UndoManager';
/*
* link doc:
* - link_anchor_1: doc
@@ -72,7 +73,7 @@ export class LinkFollower {
if (target) {
const doFollow = (canToggle?: boolean) => {
const toggleTarget = canToggle && BoolCast(sourceDoc.followLinkToggle);
- const options: DocFocusOptions = {
+ const options: FocusViewOptions = {
playAudio: BoolCast(srcAnchor.followLinkAudio),
playMedia: BoolCast(srcAnchor.followLinkVideo),
toggleTarget,
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index ccb3c6b98..dd3b9bd07 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,7 +1,7 @@
import { action, makeObservable, observable, observe, runInAction } from 'mobx';
import { computedFn } from 'mobx-utils';
import { Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
-import { DirectLinks } from '../../fields/DocSymbols';
+import { DirectLinks, DocData } from '../../fields/DocSymbols';
import { FieldLoader } from '../../fields/FieldLoader';
import { List } from '../../fields/List';
import { ProxyField } from '../../fields/Proxy';
@@ -23,8 +23,8 @@ import { ScriptingGlobals } from './ScriptingGlobals';
export class LinkManager {
@observable static _instance: LinkManager;
@observable.shallow userLinkDBs: Doc[] = [];
- @observable public static currentLink: Opt<Doc> = undefined;
- @observable public static currentLinkAnchor: Opt<Doc> = undefined;
+ @observable public currentLink: Opt<Doc> = undefined;
+ @observable public currentLinkAnchor: Opt<Doc> = undefined;
public static get Instance() {
return LinkManager._instance;
}
@@ -58,8 +58,8 @@ export class LinkManager {
link &&
action(lAnchProtoProtos => {
Doc.AddDocToList(Doc.UserDoc(), 'links', link);
- lAnchs[0] && Doc.GetProto(lAnchs[0])[DirectLinks].add(link);
- lAnchs[1] && Doc.GetProto(lAnchs[1])[DirectLinks].add(link);
+ lAnchs[0] && lAnchs[0][DocData][DirectLinks].add(link);
+ lAnchs[1] && lAnchs[1][DocData][DirectLinks].add(link);
})
)
)
@@ -74,8 +74,8 @@ export class LinkManager {
Promise.all(lAnchs.map(lAnch => PromiseValue(lAnch?.proto as Doc))).then((lAnchProtos: Opt<Doc>[]) =>
Promise.all(lAnchProtos.map(lAnchProto => PromiseValue(lAnchProto?.proto as Doc))).then(
action(lAnchProtoProtos => {
- link && lAnchs[0] && Doc.GetProto(lAnchs[0])[DirectLinks].delete(link);
- link && lAnchs[1] && Doc.GetProto(lAnchs[1])[DirectLinks].delete(link);
+ link && lAnchs[0] && lAnchs[0][DocData][DirectLinks].delete(link);
+ link && lAnchs[1] && lAnchs[1][DocData][DirectLinks].delete(link);
})
)
)
@@ -162,7 +162,7 @@ export class LinkManager {
return this.relatedLinker(anchor);
} // finds all links that contain the given anchor
public getAllDirectLinks(anchor?: Doc): Doc[] {
- return anchor ? Array.from(Doc.GetProto(anchor)[DirectLinks]) : [];
+ return anchor ? Array.from(anchor[DirectLinks]) : [];
} // finds all links that contain the given anchor
relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] {
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index dbb994dbd..f5e162d16 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -61,7 +61,8 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
const compiledFunction = (() => {
try {
return new Function(...paramNames, `return ${script}`);
- } catch {
+ } catch (e) {
+ console.log(e);
return undefined;
}
})();
@@ -248,7 +249,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
const funcScript = `(function(${paramString})${reqTypes} { ${body} })`;
host.writeFile('file.ts', funcScript);
- if (typecheck && false) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
+ if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib.default);
const program = ts.createProgram(['file.ts'], {}, host);
const testResult = program.emit();
const outputText = host.readFile('file.js');
diff --git a/src/client/util/ScrollBox.tsx b/src/client/util/ScrollBox.tsx
deleted file mode 100644
index 785526ab3..000000000
--- a/src/client/util/ScrollBox.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import * as React from 'react';
-
-export class ScrollBox extends React.Component<React.PropsWithChildren<{}>> {
- onWheel = (e: React.WheelEvent) => {
- if (e.currentTarget.scrollHeight > e.currentTarget.clientHeight) {
- // If the element has a scroll bar, then we don't want the containing collection to zoom
- e.stopPropagation();
- }
- };
-
- render() {
- return (
- <div
- style={{
- overflow: 'auto',
- width: '100%',
- height: '100%',
- }}
- onWheel={this.onWheel}>
- {this.props.children}
- </div>
- );
- }
-}
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index e51770c25..fff2737b6 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -1,53 +1,25 @@
-import * as rp from 'request-promise';
-import { DocServer } from '../DocServer';
+import { ObservableMap } from 'mobx';
import { Doc, DocListCast, Field, Opt } from '../../fields/Doc';
import { Id } from '../../fields/FieldSymbols';
-import { Utils } from '../../Utils';
-import { DocumentType } from '../documents/DocumentTypes';
import { StrCast } from '../../fields/Types';
+import { DocumentType } from '../documents/DocumentTypes';
+import { DocOptions, FInfo } from '../documents/Documents';
export namespace SearchUtil {
export type HighlightingResult = { [id: string]: { [key: string]: string[] } };
- export function SearchCollection(collectionDoc: Opt<Doc>, query: string) {
+ export function SearchCollection(collectionDoc: Opt<Doc>, query: string, matchKeyNames: boolean, onlyKeys?: string[]) {
const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
- const blockedKeys = [
- 'x',
- 'y',
- 'proto',
- 'width',
- 'layout_autoHeight',
- 'acl-Override',
- 'acl-Guest',
- 'embedContainer',
- 'zIndex',
- 'height',
- 'text_scrollHeight',
- 'text_height',
- 'cloneFieldFilter',
- 'isDataDoc',
- 'text_annotations',
- 'dragFactory_count',
- 'text_noTemplate',
- 'proto_embeddings',
- 'isSystem',
- 'layout_fieldKey',
- 'isBaseProto',
- 'xMargin',
- 'yMargin',
- 'links',
- 'layout',
- 'layout_keyValue',
- 'layout_fitWidth',
- 'type_collection',
- 'title_custom',
- 'freeform_panX',
- 'freeform_panY',
- 'freeform_scale',
- ];
- query = query.toLowerCase();
+ const blockedKeys = matchKeyNames
+ ? []
+ : Object.entries(DocOptions)
+ .filter(([key, info]: [string, FInfo]) => !info?.searchable())
+ .map(([key]) => key);
- const results = new Map<Doc, string[]>();
+ const exact = query.startsWith('=');
+ query = query.toLowerCase().split('=').lastElement();
+
+ const results = new ObservableMap<Doc, string[]>();
if (collectionDoc) {
const docs = DocListCast(collectionDoc[Doc.LayoutFieldKey(collectionDoc)]);
const docIDs: String[] = [];
@@ -55,12 +27,12 @@ export namespace SearchUtil {
const dtype = StrCast(doc.type) as DocumentType;
if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth >= 0) {
const hlights = new Set<string>();
- SearchUtil.documentKeys(doc).forEach(
+ (onlyKeys ?? SearchUtil.documentKeys(doc)).forEach(
key =>
- (Field.toString(doc[key] as Field) + Field.toScriptString(doc[key] as Field))
- .toLowerCase() //
- .includes(query) && hlights.add(key)
- );
+ (val => (exact ? val === query.toLowerCase() : val.includes(query.toLowerCase())))(
+ matchKeyNames ? key : Field.toString(doc[key] as Field))
+ && hlights.add(key)
+ ); // prettier-ignore
blockedKeys.forEach(key => hlights.delete(key));
if (Array.from(hlights.keys()).length > 0) {
@@ -110,154 +82,4 @@ export namespace SearchUtil {
depth++;
}
}
- export interface IdSearchResult {
- ids: string[];
- lines: string[][];
- numFound: number;
- highlighting: HighlightingResult | undefined;
- }
-
- export interface DocSearchResult {
- docs: Doc[];
- lines: string[][];
- numFound: number;
- highlighting: HighlightingResult | undefined;
- }
-
- export interface SearchParams {
- hl?: string;
- 'hl.fl'?: string;
- start?: number;
- rows?: number;
- fq?: string;
- sort?: string;
- allowEmbeddings?: boolean;
- onlyEmbeddings?: boolean;
- facet?: string;
- 'facet.field'?: string;
- }
- export function Search(query: string, returnDocs: true, options?: SearchParams): Promise<DocSearchResult>;
- export function Search(query: string, returnDocs: false, options?: SearchParams): Promise<IdSearchResult>;
- export async function Search(query: string, returnDocs: boolean, options: SearchParams = {}) {
- query = query || '*'; //If we just have a filter query, search for * as the query
- const rpquery = Utils.prepend('/dashsearch');
- let replacedQuery = query.replace(/type_t:([^ )])/g, (substring, arg) => `{!join from=id to=proto_i}*:* AND ${arg}`);
- if (options.onlyEmbeddings) {
- const header = query.match(/_[atnb]?:/) ? replacedQuery : 'DEFAULT:' + replacedQuery;
- replacedQuery = `{!join from=id to=proto_i}* AND ${header}`;
- }
- //console.log("Q: " + replacedQuery + " fq: " + options.fq);
- const gotten = await rp.get(rpquery, { qs: { ...options, q: replacedQuery } });
- const result: IdSearchResult = gotten.startsWith('<') ? { ids: [], docs: [], numFound: 0, lines: [] } : JSON.parse(gotten);
- if (!returnDocs) {
- return result;
- }
-
- const { ids, highlighting } = result;
-
- const txtresult =
- query !== '*' &&
- JSON.parse(
- await rp.get(Utils.prepend('/textsearch'), {
- qs: { ...options, q: query.replace(/^[ \+\?\*\|]*/, '') }, // a leading '+' leads to a server crash since findInFiles doesn't handle regex failures
- })
- );
-
- const fileids = txtresult ? txtresult.ids : [];
- const newIds: string[] = [];
- const newLines: string[][] = [];
- // bcz: we stopped storing fileUpload id's, so this won't find anything
- // if (fileids) {
- // await Promise.all(
- // fileids.map(async (tr: string, i: number) => {
- // const docQuery = 'fileUpload_t:' + tr.substr(0, 7); //If we just have a filter query, search for * as the query
- // const docResult = JSON.parse(await rp.get(Utils.prepend('/dashsearch'), { qs: { ...options, q: docQuery } }));
- // newIds.push(...docResult.ids);
- // newLines.push(...docResult.ids.map((dr: any) => txtresult.lines[i]));
- // })
- // );
- // }
-
- const theDocs: Doc[] = [];
- const theLines: string[][] = [];
- const textDocMap = await DocServer.GetRefFields(newIds);
- const textDocs = newIds.map((id: string) => textDocMap[id]).map(doc => doc as Doc);
- for (let i = 0; i < textDocs.length; i++) {
- const testDoc = textDocs[i];
- if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1) {
- theDocs.push(Doc.GetProto(testDoc));
- theLines.push(newLines[i].map(line => line.replace(query, query.toUpperCase())));
- }
- }
-
- const docMap = await DocServer.GetRefFields(ids);
- const docs = ids.map((id: string) => docMap[id]).map(doc => doc as Doc);
- for (let i = 0; i < ids.length; i++) {
- const testDoc = docs[i];
- if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowEmbeddings || testDoc.proto === undefined || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) {
- theDocs.push(testDoc);
- theLines.push([]);
- } else {
- result.numFound--;
- }
- }
-
- return { docs: theDocs, numFound: Math.max(0, result.numFound), highlighting, lines: theLines };
- }
-
- export async function GetEmbeddingsOfDocument(doc: Doc): Promise<Doc[]>;
- export async function GetEmbeddingsOfDocument(doc: Doc, returnDocs: false): Promise<string[]>;
- export async function GetEmbeddingsOfDocument(doc: Doc, returnDocs = true): Promise<Doc[] | string[]> {
- const proto = Doc.GetProto(doc);
- const protoId = proto[Id];
- if (returnDocs) {
- return (await Search('', returnDocs, { fq: `proto_i:"${protoId}"`, allowEmbeddings: true })).docs;
- } else {
- return (await Search('', returnDocs, { fq: `proto_i:"${protoId}"`, allowEmbeddings: true })).ids;
- }
- // return Search(`{!join from=id to=proto_i}id:${protoId}`, true);
- }
-
- export async function GetViewsOfDocument(doc: Doc): Promise<Doc[]> {
- const results = await Search('', true, { fq: `proto_i:"${doc[Id]}"` });
- return results.docs;
- }
-
- export async function GetContextsOfDocument(doc: Doc): Promise<{ contexts: Doc[]; embeddingContexts: Doc[] }> {
- const docContexts = (await Search('', true, { fq: `data_l:"${doc[Id]}"` })).docs;
- const embeddings = await GetEmbeddingsOfDocument(doc, false);
- const embeddingContexts = await Promise.all(embeddings.map(doc => Search('', true, { fq: `data_l:"${doc}"` })));
- const contexts = { contexts: docContexts, embeddingContexts: [] as Doc[] };
- embeddingContexts.forEach(result => contexts.embeddingContexts.push(...result.docs));
- return contexts;
- }
-
- export async function GetContextIdsOfDocument(doc: Doc): Promise<{ contexts: string[]; embeddingContexts: string[] }> {
- const docContexts = (await Search('', false, { fq: `data_l:"${doc[Id]}"` })).ids;
- const embeddings = await GetEmbeddingsOfDocument(doc, false);
- const embeddingContexts = await Promise.all(embeddings.map(doc => Search('', false, { fq: `data_l:"${doc}"` })));
- const contexts = { contexts: docContexts, embeddingContexts: [] as string[] };
- embeddingContexts.forEach(result => contexts.embeddingContexts.push(...result.ids));
- return contexts;
- }
-
- export async function GetAllDocs() {
- const query = '*';
- const response = await rp.get(Utils.prepend('/dashsearch'), {
- qs: { start: 0, rows: 10000, q: query },
- });
- const result: IdSearchResult = JSON.parse(response);
- const { ids, numFound, highlighting } = result;
- const docMap = await DocServer.GetRefFields(ids);
- const docs: Doc[] = [];
- for (const id of ids) {
- const field = docMap[id];
- if (field instanceof Doc) {
- docs.push(field);
- }
- }
- return docs;
- // const docs = ids.map((id: string) => docMap[id]).filter((doc: any) => doc instanceof Doc);
- // return docs as Doc[];
- }
}
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index f2a327445..36b926053 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -38,6 +38,7 @@ export class SelectionManager {
this.Instance.SelectedViews.push(docView);
docView.IsSelected = true;
docView._props.whenChildContentsActiveChanged(true);
+ docView.ComponentView?.select?.(false, false);
}
});
@@ -51,9 +52,11 @@ export class SelectionManager {
public static DeselectAll = (except?: Doc): void => {
const found = this.Instance.SelectedViews.find(dv => dv.Document === except);
- LinkManager.currentLink = undefined;
- LinkManager.currentLinkAnchor = undefined;
- runInAction(() => (this.Instance.SelectedSchemaDocument = undefined));
+ runInAction(() => {
+ LinkManager.Instance.currentLink = undefined;
+ LinkManager.Instance.currentLinkAnchor = undefined;
+ this.Instance.SelectedSchemaDocument = undefined;
+ });
this.Instance.SelectedViews.forEach(dv => {
dv.IsSelected = false;
dv._props.whenChildContentsActiveChanged(false);
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 0233c4051..682704770 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -5,13 +5,13 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { BsGoogle } from 'react-icons/bs';
import { FaFillDrip, FaPalette } from 'react-icons/fa';
+import { Utils, addStyleSheet, addStyleSheetRule } from '../../Utils';
import { Doc, Opt } from '../../fields/Doc';
import { DashVersion } from '../../fields/DocSymbols';
import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types';
-import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
-import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { DocServer } from '../DocServer';
import { Networking } from '../Network';
+import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { GestureOverlay } from '../views/GestureOverlay';
import { MainViewModal } from '../views/MainViewModal';
import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox';
@@ -47,7 +47,6 @@ export class SettingsManager extends React.Component<{}> {
@observable activeTab = 'Accounts';
@observable public propertiesWidth: number = 0;
- @observable public headerBarHeight: number = 0;
constructor(props: {}) {
super(props);
@@ -318,7 +317,17 @@ export class SettingsManager extends React.Component<{}> {
<div className="tab-column-content">
{/* <NumberInput/> */}
<Group formLabel={'Default Font'}>
- <NumberDropdown color={SettingsManager.userColor} numberDropdownType={'input'} min={0} max={50} step={2} type={Type.TERT} number={0} unit={'px'} setNumber={() => {}} />
+ <NumberDropdown
+ color={SettingsManager.userColor}
+ numberDropdownType="slider"
+ min={0}
+ max={50}
+ step={2}
+ type={Type.PRIM}
+ number={NumCast(Doc.UserDoc().fontSize, Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')))}
+ unit={'px'}
+ setNumber={val => (Doc.UserDoc().fontSize = val + 'px')}
+ />
<Dropdown
items={fontFamilies.map(val => {
return {
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 9a6719ea5..421855bf3 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -102,7 +102,7 @@ export namespace UndoManager {
'UndoEvent : ' +
event.prop +
' = ' +
- (value instanceof RichTextField ? value.Text : value instanceof Array ? value.map(val => Field.toScriptString(val)).join(',') : Field.toScriptString(value))
+ (value instanceof RichTextField ? value.Text : value instanceof Array ? value.map(val => Field.toJavascriptString(val)).join(',') : Field.toJavascriptString(value))
);
currentBatch.push(event);
tempEvents?.push(event);