From 42afc0250de658fc3e924864bfae5afb4edec335 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 14 May 2023 12:03:40 -0400 Subject: major overhaul of field naming conventions. --- .../collections/CollectionStackedTimeline.tsx | 39 ++++++++++++---------- 1 file changed, 21 insertions(+), 18 deletions(-) (limited to 'src/client/views/collections/CollectionStackedTimeline.tsx') diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 22a575989..6b4c8a3e9 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -24,7 +24,7 @@ import { undoBatch, UndoManager } from '../../util/UndoManager'; import { AudioWaveform } from '../AudioWaveform'; import { CollectionSubView } from '../collections/CollectionSubView'; import { LightboxView } from '../LightboxView'; -import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView'; +import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../nodes/DocumentView'; import { LabelBox } from '../nodes/LabelBox'; import { VideoBox } from '../nodes/VideoBox'; import './CollectionStackedTimeline.scss'; @@ -41,7 +41,9 @@ export type CollectionStackedTimelineProps = { mediaPath: string; dictationKey: string; rawDuration: number; + dataFieldKey: string; fieldKey: string; + thumbnails?: () => string[]; }; // trimming state: shows full clip, current trim bounds, or not trimming @@ -80,6 +82,9 @@ export class CollectionStackedTimeline extends CollectionSubView 0 ? new ImageField(thumbnails[nearest]) : undefined; + const imgField = this.thumbnails.length > 0 ? new ImageField(this.thumbnails[nearest]) : undefined; this._thumbnail = imgField?.url?.href ? imgField.url.href.replace('.png', '_m.png') : undefined; } } @@ -401,11 +405,9 @@ export class CollectionStackedTimeline extends CollectionSubView NumCast(this.layoutDoc._currentTimecode)) { + if (seekTimeInSeconds < NumCast(this.layoutDoc._layout_currentTimecode) && endTime > NumCast(this.layoutDoc._layout_currentTimecode)) { if (!this.layoutDoc.autoPlayAnchors && this.props.playing()) { this.props.Pause(); } else { @@ -455,7 +457,7 @@ export class CollectionStackedTimeline extends CollectionSubView NumCast(this.layoutDoc._currentTimecode) - 1e-4) { + if (seekTimeInSeconds < NumCast(this.layoutDoc._layout_currentTimecode) + 1e-4 && endTime > NumCast(this.layoutDoc._layout_currentTimecode) - 1e-4) { if (this.props.playing()) this.props.Pause(); else if (this.layoutDoc.autoPlayAnchors) this.props.Play(); else if (!this.layoutDoc.autoPlayAnchors) { @@ -596,6 +598,7 @@ export class CollectionStackedTimeline extends CollectionSubView void; + addDocTab: (doc: Doc, where: OpenWhere) => boolean; rangeClickScript: () => ScriptField; rangePlayScript: () => ScriptField; left: number; @@ -709,7 +714,7 @@ class StackedTimelineAnchor extends React.Component () => this.props.currentTimecode(), time => { const dictationDoc = Cast(this.props.layoutDoc['data-dictation'], Doc, null); - const isDictation = dictationDoc && LinkManager.Links(this.props.mark).some(link => Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc); + const isDictation = dictationDoc && LinkManager.Links(this.props.mark).some(link => Cast(link.link_anchor_1, Doc, null)?.annotationOn === dictationDoc); if ( !LightboxView.LightboxDoc && // bcz: when should links be followed? we don't want to move away from the video to follow a link but we can open it in a sidebar/etc. But we don't know that upfront. @@ -808,11 +813,9 @@ class StackedTimelineAnchor extends React.Component isDocumentActive={this.props.isDocumentActive} PanelWidth={width} PanelHeight={height} - fitWidth={returnTrue} + layout_fitWidth={returnTrue} ScreenToLocalTransform={screenXf} - addDocTab={returnFalse} pinToPres={emptyFunction} - whenChildContentsActiveChanged={emptyFunction} focus={focusFunc} isContentActive={returnFalse} searchFilterDocs={returnEmptyDoclist} -- cgit v1.2.3-70-g09d2 From 46cf6c823ca8ab628cd8c5bd7fdfe8945344a014 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 16 May 2023 14:50:29 -0400 Subject: fixed bugs with goldenlayout dragging and undoing. fixed searching for filter values in sidebars. Stopped creating empty list for collections when datafield() is accessed because it messes up undo of a collection. fixed tab title editing. from marquee. Added UndoStack UI and additional naming support in code. --- src/client/documents/Documents.ts | 12 ++-- src/client/goldenLayout.js | 2 +- src/client/util/CurrentUserUtils.ts | 44 +++++++------- src/client/util/DragManager.ts | 6 +- src/client/util/LinkFollower.ts | 2 +- src/client/util/SelectionManager.ts | 2 +- src/client/util/UndoManager.ts | 53 ++++++++++++----- src/client/views/ContextMenuItem.tsx | 5 +- src/client/views/DocumentDecorations.tsx | 46 +++++---------- src/client/views/EditableView.tsx | 2 + src/client/views/FilterPanel.tsx | 9 ++- src/client/views/GlobalKeyHandler.ts | 10 ++-- src/client/views/MainView.tsx | 8 +-- src/client/views/PropertiesButtons.tsx | 6 +- .../views/PropertiesDocBacklinksSelector.tsx | 2 +- src/client/views/PropertiesDocContextSelector.tsx | 2 +- src/client/views/PropertiesView.tsx | 32 +++++----- src/client/views/SidebarAnnos.scss | 14 +++-- src/client/views/SidebarAnnos.tsx | 15 ++++- src/client/views/UndoStack.tsx | 30 ++++++++++ .../views/collections/CollectionDockingView.tsx | 63 ++++++++++---------- .../collections/CollectionNoteTakingViewColumn.tsx | 5 +- .../collections/CollectionStackedTimeline.tsx | 3 +- .../CollectionStackingViewFieldColumn.tsx | 4 +- src/client/views/collections/CollectionSubView.tsx | 13 ++-- src/client/views/collections/TabDocView.tsx | 21 ++++--- src/client/views/collections/TreeView.tsx | 4 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 - .../collections/collectionFreeForm/MarqueeView.tsx | 26 +++----- .../collectionSchema/CollectionSchemaView.tsx | 32 +++++----- .../collectionSchema/SchemaColumnHeader.tsx | 2 - .../collections/collectionSchema/SchemaRowBox.tsx | 10 ++-- .../collectionSchema/SchemaTableCell.tsx | 2 +- src/client/views/linking/LinkMenu.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 2 +- src/client/views/nodes/DocumentLinksButton.tsx | 20 +++---- src/client/views/nodes/DocumentView.tsx | 57 ++++++++++-------- src/client/views/nodes/LinkDocPreview.tsx | 3 +- src/client/views/nodes/button/FontIconBox.tsx | 60 ++++++++----------- .../views/nodes/formattedText/FormattedTextBox.tsx | 69 +++++++++++----------- src/client/views/nodes/trails/PresBox.tsx | 9 ++- src/client/views/nodes/trails/PresElementBox.tsx | 13 ++-- src/client/views/pdf/AnchorMenu.tsx | 11 ++-- src/client/views/search/SearchBox.tsx | 2 + src/fields/Doc.ts | 18 ++++-- src/fields/util.ts | 38 +++++++----- 46 files changed, 421 insertions(+), 372 deletions(-) create mode 100644 src/client/views/UndoStack.tsx (limited to 'src/client/views/collections/CollectionStackedTimeline.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2151d0ec7..795d60b48 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -24,7 +24,7 @@ import { DirectoryImportBox } from '../util/Import & Export/DirectoryImportBox'; import { FollowLinkScript } from '../util/LinkFollower'; import { LinkManager } from '../util/LinkManager'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; -import { undoBatch, UndoManager } from '../util/UndoManager'; +import { undoable, undoBatch, UndoManager } from '../util/UndoManager'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView'; import { CollectionView } from '../views/collections/CollectionView'; @@ -249,7 +249,7 @@ export class DocumentOptions { contextMenuIcons?: List; defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire. 'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait - dontUndo?: boolean; // whether button clicks should be undoable (this is set to true for Undo/Redo/and sidebar buttons that open the siebar panel) + dontUndo?: boolean; // whether button clicks should be undoable ( true for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack) layout?: string | Doc; // default layout string for a document contentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents childLimitHeight?: number; // whether to limit the height of collection children. 0 - means height can be no bigger than width @@ -1532,7 +1532,7 @@ export namespace DocUtils { description: 'Quick Notes', subitems: DocListCast((Doc.UserDoc()['template-notes'] as Doc).data).map((note, i) => ({ description: ':' + StrCast(note.title), - event: undoBatch((args: { x: number; y: number }) => { + event: undoable((args: { x: number; y: number }) => { const textDoc = Docs.Create.TextDocument('', { _width: 200, x, @@ -1546,7 +1546,7 @@ export namespace DocUtils { textDoc[pivotField] = pivotValue; } docTextAdder(textDoc); - }), + }, 'create quick note'), icon: StrCast(note.icon) as IconProp, })) as ContextMenuProps[], icon: 'sticky-note', @@ -1557,7 +1557,7 @@ export namespace DocUtils { .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc !== Doc.UserDoc().emptyDataViz) .map((dragDoc, i) => ({ description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), - event: undoBatch((args: { x: number; y: number }) => { + event: undoable((args: { x: number; y: number }) => { const newDoc = DocUtils.copyDragFactory(dragDoc); if (newDoc) { newDoc.author = Doc.CurrentUserEmail; @@ -1570,7 +1570,7 @@ export namespace DocUtils { } docAdder?.(newDoc); } - }), + }, StrCast(dragDoc.title)), icon: Doc.toIcon(dragDoc), })) as ContextMenuProps[]; ContextMenu.Instance.addItem({ diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js index 9cb20d834..843b8bb5f 100644 --- a/src/client/goldenLayout.js +++ b/src/client/goldenLayout.js @@ -4740,7 +4740,7 @@ newstack._$init(); newstack.addChild(this.contentItems[0]); } - correctRowOrCol.addChild(newstack, insertBefore ? 0 : undefined, true); + correctRowOrCol.addChild(newstack, !insertBefore ? 0 : undefined, true); newstack.config[dimension] = 50; contentItem.config[dimension] = 50; correctRowOrCol.callDownwards('setSize'); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 26fe8f440..b0a2c7d60 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -363,7 +363,7 @@ export class CurrentUserUtils { const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, scripts, funcs }) => { const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined; const reqdBtnOpts:DocumentOptions = { - title, icon, target, btnType: ButtonType.MenuButton, isSystem: true, dontUndo: true, dontRegisterView: true, + title, icon, target, btnType: ButtonType.MenuButton, isSystem: true, dontUndo: true, dontRegisterView: true, _width: 60, _height: 60, _stayInCollection: true, _hideContextMenu: true, _removeDropProperties: new List(["_stayInCollection"]), }; @@ -598,12 +598,12 @@ 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: "Click to undo" }}, - { scripts: { onClick: "redo()"}, opts: { title: "redo", icon: "redo-alt", toolTip: "Click to redo" }}, - { scripts: { }, opts: { title: "linker", icon: "linkui", toolTip: "link started"}}, - { scripts: { }, opts: { title: "currently playing", icon: "currentlyplayingui", toolTip: "currently playing media"}}, + { scripts: { onClick: "undo()"}, opts: { title: "undo", icon: "undo-alt", toolTip: "Click to undo" }}, + { scripts: { onClick: "redo()"}, opts: { title: "redo", icon: "redo-alt", toolTip: "Click to redo" }}, + { scripts: { }, opts: { title: "linker", icon: "linkui", toolTip: "link started"}}, + { scripts: { }, opts: { title: "currently playing", icon: "currentlyplayingui", toolTip: "currently playing media"}}, ]; - const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts)); + const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, defaultDoubleClick: 'ignore', dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts)); const dockBtnsReqdOpts:DocumentOptions = { title: "docked buttons", _height: 40, flexGap: 0, boxShadow: "standard", childDropAction: 'embed', childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true @@ -632,21 +632,21 @@ export class CurrentUserUtils { } static textTools():Button[] { return [ - { title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, toolType:"font", ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, + { title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, toolType:"font", ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, btnList: new List(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) }, - { title: "Size", toolTip: "Font size (%size)", btnType: ButtonType.NumberDropdownButton, width: 75, toolType:"fontSize", ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0 }, - { title: "Color", toolTip: "Font color (%color)", btnType: ButtonType.ColorButton, icon: "font", toolType:"fontColor",ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}}, + { title: "Font Size",toolTip: "Font size (%size)", btnType: ButtonType.NumberDropdownButton, width: 75, toolType:"fontSize", ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0 }, + { title: "Color", toolTip: "Font color (%color)", btnType: ButtonType.ColorButton, icon: "font", toolType:"fontColor",ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}}, { title: "Highlight",toolTip:"Font highlight", btnType: ButtonType.ColorButton, icon: "highlighter", toolType:"highlight",ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'},funcs: {hidden: "IsNoviceMode()"} }, - { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", toolType:"bold", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, - { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", toolType:"italics", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, - { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", toolType:"underline", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, - { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", toolType:"bullet", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, - { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", toolType:"decimal", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, - { title: "Left", toolTip: "Left align (Cmd-[)", btnType: ButtonType.ToggleButton, icon: "align-left", toolType:"left", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }}, - { title: "Center", toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, - { title: "Right", toolTip: "Right align (Cmd-])", btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, - { title: "Dictate", toolTip: "Dictate", btnType: ButtonType.ToggleButton, icon: "microphone", toolType:"dictation", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}}, - { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", toolType:"noAutoLink", expertMode:true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}, funcs: {hidden: 'IsNoviceMode()'}}, + { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", toolType:"bold", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, + { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", toolType:"italics", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, + { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", toolType:"underline", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, + { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", toolType:"bullet", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, + { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", toolType:"decimal", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, + { title: "Left", toolTip: "Left align (Cmd-[)", btnType: ButtonType.ToggleButton, icon: "align-left", toolType:"left", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }}, + { title: "Center", toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, + { title: "Right", toolTip: "Right align (Cmd-])", btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, + { title: "Dictate", toolTip: "Dictate", btnType: ButtonType.ToggleButton, icon: "microphone", toolType:"dictation", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}}, + { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", toolType:"noAutoLink", expertMode:true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}, funcs: {hidden: 'IsNoviceMode()'}}, // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", scripts: {onClick:: 'toggleStrikethrough()'}}, // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", scripts: {onClick:: 'toggleSuperscript()'}}, // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", scripts: {onClick:: 'toggleSubscript()'}}, @@ -711,7 +711,7 @@ export class CurrentUserUtils { const reqdOpts:DocumentOptions = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, backgroundColor: params.backgroundColor ??"transparent", /// a bit hacky. if an onClick is specified, then assume a toggle uses onClick to get the backgroundColor (see below). Otherwise, assume a transparent background - color: Colors.WHITE, isSystem: true, dontUndo: true, + color: Colors.WHITE, isSystem: true, //dontUndo: true, _nativeWidth: params.width ?? 30, _width: params.width ?? 30, _height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth, toolType: params.toolType, expertMode: params.expertMode, @@ -727,14 +727,14 @@ 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", flexGap: 0, childDropAction: 'embed', childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }; + const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", dontUndo:true, flexGap: 0, childDropAction: 'embed', childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }; const ctxtMenuBtnsDoc = DocUtils.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, + const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, dontUndo: true, childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: params.scripts?.onClick ? false : true, linearViewSubMenu: true, linearViewExpandable: true, }; const items = params.subMenu?.map(sub => diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 070e0f918..e3798233e 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -263,7 +263,7 @@ export namespace DragManager { // drags a column from a schema view export function StartColumnDrag(ele: HTMLElement[], dragData: ColumnDragData, downX: number, downY: number, options?: DragOptions) { - StartDrag(ele, dragData, downX, downY, options); + StartDrag(ele, dragData, downX, downY, options, undefined, 'Drag Column'); } export function SetSnapLines(horizLines: number[], vertLines: number[]) { @@ -325,10 +325,10 @@ export namespace DragManager { export let docsBeingDragged: Doc[] = observable([] as Doc[]); export let CanEmbed = false; export let DocDragData: DocumentDragData | undefined; - export function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) { + export function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void, dragUndoName?:string) { if (dragData.dropAction === 'none') return; DocDragData = dragData as DocumentDragData; - const batch = UndoManager.StartBatch('dragging'); + const batch = UndoManager.StartBatch(dragUndoName ?? 'document drag'); eles = eles.filter(e => e); CanEmbed = dragData.canEmbed || false; if (!dragDiv) { diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index 2812d6c88..f74409e42 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -28,7 +28,7 @@ export class LinkFollower { // if the target isn't onscreen, then it will open up the target in the lightbox, or in place // depending on the followLinkLocation property of the source (or the link itself as a fallback); public static FollowLink = (linkDoc: Opt, sourceDoc: Doc, altKey: boolean) => { - const batch = UndoManager.StartBatch('follow link click'); + const batch = UndoManager.StartBatch('Follow Link'); runInAction(() => (LinkFollower.IsFollowing = true)); // turn off decoration bounds while following links since animations may occur, and DocDecorations is based on screenToLocal which is not always an observable value LinkFollower.traverseLink( linkDoc, diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index fba0a4f76..0125331ec 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -118,6 +118,6 @@ ScriptingGlobals.add(function SelectionManager_selectedDocType(type: string, exp if (type === 'tab') { return SelectionManager.Views().lastElement()?.props.renderDepth === 0; } - let selected = (sel => (checkContext ? DocCast(sel?.context) : sel))(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement()); + let selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement()); return selected?.type === type || selected?.type_collection === type || !type; }); diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts index d0aec45a6..6fef9d660 100644 --- a/src/client/util/UndoManager.ts +++ b/src/client/util/UndoManager.ts @@ -3,7 +3,7 @@ import { Without } from '../../Utils'; function getBatchName(target: any, key: string | symbol): string { const keyName = key.toString(); - if (target && target.constructor && target.constructor.name) { + if (target?.constructor?.name) { return `${target.constructor.name}.${keyName}`; } return keyName; @@ -34,6 +34,17 @@ function propertyDecorator(target: any, key: string | symbol) { }); } +export function undoable(fn: (...args: any[]) => any, batchName: string): (...args: any[]) => any { + return function () { + const batch = UndoManager.StartBatch(batchName); + try { + return fn.apply(undefined, arguments as any); + } finally { + batch.end(); + } + }; +} + export function undoBatch(target: any, key: string | symbol, descriptor?: TypedPropertyDescriptor): any; export function undoBatch(fn: (...args: any[]) => any): (...args: any[]) => any; export function undoBatch(target: any, key?: string | symbol, descriptor?: TypedPropertyDescriptor): any { @@ -73,15 +84,18 @@ export namespace UndoManager { } type UndoBatch = UndoEvent[]; + export let undoStackNames: string[] = observable([]); + export let redoStackNames: string[] = observable([]); export let undoStack: UndoBatch[] = observable([]); export let redoStack: UndoBatch[] = observable([]); let currentBatch: UndoBatch | undefined; - export let batchCounter = 0; + export let batchCounter = observable.box(0); let undoing = false; let tempEvents: UndoEvent[] | undefined = undefined; - export function AddEvent(event: UndoEvent): void { - if (currentBatch && batchCounter && !undoing) { + export function AddEvent(event: UndoEvent, value?: any): void { + if (currentBatch && batchCounter.get() && !undoing) { + console.log(' '.slice(0, batchCounter.get()) + 'UndoEvent : ' + event.prop + ' = ' + value); currentBatch.push(event); tempEvents?.push(event); } @@ -135,11 +149,13 @@ export namespace UndoManager { private dispose = (cancel: boolean) => { if (this.disposed) { - throw new Error('Cannot dispose an already disposed batch'); + console.log('WARNING: undo batch already disposed'); + return false; + } else { + this.disposed = true; + openBatches.splice(openBatches.indexOf(this)); + return EndBatch(this.batchName, cancel); } - this.disposed = true; - openBatches.splice(openBatches.indexOf(this)); - return EndBatch(cancel); }; end = () => this.dispose(false); @@ -147,22 +163,23 @@ export namespace UndoManager { } export function StartBatch(batchName: string): Batch { - // console.log("Start " + batchCounter + " " + batchName); - batchCounter++; - if (batchCounter > 0 && currentBatch === undefined) { + console.log(' '.slice(0, batchCounter.get()) + 'Start ' + batchCounter + ' ' + batchName); + runInAction(() => batchCounter.set(batchCounter.get() + 1)); + if (currentBatch === undefined) { currentBatch = []; } return new Batch(batchName); } - const EndBatch = action((cancel: boolean = false) => { - batchCounter--; - // console.log("End " + batchCounter); - if (batchCounter === 0 && currentBatch?.length) { - // console.log("------ended----") + const EndBatch = action((batchName: string, cancel: boolean = false) => { + runInAction(() => batchCounter.set(batchCounter.get() - 1)); + console.log(' '.slice(0, batchCounter.get()) + 'End ' + batchName + ' (' + currentBatch?.length + ')'); + if (batchCounter.get() === 0 && currentBatch?.length) { if (!cancel) { undoStack.push(currentBatch); + undoStackNames.push(batchName ?? '???'); } + redoStackNames.length = 0; redoStack.length = 0; currentBatch = undefined; return true; @@ -204,6 +221,7 @@ export namespace UndoManager { return; } + const names = undoStackNames.pop(); const commands = undoStack.pop(); if (!commands) { return; @@ -215,6 +233,7 @@ export namespace UndoManager { } undoing = false; + redoStackNames.push(names ?? '???'); redoStack.push(commands); }); @@ -223,6 +242,7 @@ export namespace UndoManager { return; } + const names = redoStackNames.pop(); const commands = redoStack.pop(); if (!commands) { return; @@ -234,6 +254,7 @@ export namespace UndoManager { } undoing = false; + undoStackNames.push(names ?? '???'); undoStack.push(commands); }); } diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index e87d2046b..33f250986 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -40,10 +40,7 @@ export class ContextMenuItem extends React.Component) => { if ('event' in this.props) { this.props.closeMenu?.(); - let batch: UndoManager.Batch | undefined; - if (this.props.undoable !== false) { - batch = UndoManager.StartBatch(`Context menu event: ${this.props.description}`); - } + const batch = this.props.undoable !== false ? UndoManager.StartBatch(`Click Menu item: ${this.props.description}`) : undefined; await this.props.event({ x: e.clientX, y: e.clientY }); batch?.end(); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 39073d763..8077b9af1 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -2,23 +2,25 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import { IconButton } from 'browndash-components'; -import { action, computed, observable, reaction, runInAction } from 'mobx'; +import { action, computed, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { FaUndo } from 'react-icons/fa'; import { DateField } from '../../fields/DateField'; import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from '../../fields/Doc'; -import { Document } from '../../fields/documentSchemas'; import { InkField } from '../../fields/InkField'; +import { RichTextField } from '../../fields/RichTextField'; import { ScriptField } from '../../fields/ScriptField'; import { Cast, NumCast, StrCast } from '../../fields/Types'; import { GetEffectiveAcl } from '../../fields/util'; import { aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils'; import { Docs } from '../documents/Documents'; import { DocumentType } from '../documents/DocumentTypes'; +import { DocumentManager } from '../util/DocumentManager'; import { DragManager } from '../util/DragManager'; +import { LinkFollower } from '../util/LinkFollower'; import { SelectionManager } from '../util/SelectionManager'; import { SnappingManager } from '../util/SnappingManager'; -import { undoBatch, UndoManager } from '../util/UndoManager'; +import { UndoManager } from '../util/UndoManager'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { DocumentButtonBar } from './DocumentButtonBar'; @@ -27,15 +29,11 @@ import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; import { InkStrokeProperties } from './InkStrokeProperties'; import { LightboxView } from './LightboxView'; -import { DocumentView, OpenWhere, OpenWhereMod } from './nodes/DocumentView'; +import { DocumentView, OpenWhereMod } from './nodes/DocumentView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { ImageBox } from './nodes/ImageBox'; import React = require('react'); -import { RichTextField } from '../../fields/RichTextField'; -import { LinkFollower } from '../util/LinkFollower'; import _ = require('lodash'); -import { DocumentManager } from '../util/DocumentManager'; -import { isUndefined } from 'lodash'; @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> { @@ -150,7 +148,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true); } }), - 'title blur' + 'edit title' ); } }; @@ -227,11 +225,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P .filter(v => v && v.props.renderDepth > 0); if (forceDeleteOrIconify === false && this._iconifyBatch) return; this._deleteAfterIconify = forceDeleteOrIconify || this._iconifyBatch ? true : false; - if (!this._iconifyBatch) { - this._iconifyBatch = UndoManager.StartBatch('iconifying'); - } else { - forceDeleteOrIconify = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes - } var iconifyingCount = views.length; const finished = action((force?: boolean) => { if ((force || --iconifyingCount === 0) && this._iconifyBatch) { @@ -250,6 +243,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P this._iconifyBatch = undefined; } }); + if (!this._iconifyBatch) { + this._iconifyBatch = UndoManager.StartBatch(forceDeleteOrIconify ? 'delete selected docs' : 'iconifying'); + } else { + forceDeleteOrIconify = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes + } + if (forceDeleteOrIconify) finished(forceDeleteOrIconify); else if (!this._deleteAfterIconify) views.forEach(dv => dv.iconify(finished)); }; @@ -397,7 +396,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P onRotateDown = (e: React.PointerEvent): void => { this._isRotating = true; const rcScreen = { X: this.rotCenter[0], Y: this.rotCenter[1] }; - const rotateUndo = UndoManager.StartBatch('rotatedown'); + const rotateUndo = UndoManager.StartBatch('drag rotation'); const selectedInk = SelectionManager.Views().filter(i => i.ComponentView instanceof InkingStroke); const centerPoint = this.rotCenter.slice(); const infos = new Map(); @@ -465,7 +464,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const bounds = e.currentTarget.getBoundingClientRect(); this._offX = this._resizeHdlId.toLowerCase().includes('left') ? bounds.right - e.clientX : bounds.left - e.clientX; this._offY = this._resizeHdlId.toLowerCase().includes('top') ? bounds.bottom - e.clientY : bounds.top - e.clientY; - this._resizeUndo = UndoManager.StartBatch('DocDecs resize'); + this._resizeUndo = UndoManager.StartBatch('drag resizing'); this._snapX = e.pageX; this._snapY = e.pageY; const ffviewSet = new Set(); @@ -770,20 +769,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => ( {title}} placement="top"> -
e.preventDefault()} - onPointerDown={ - pointerDown ?? - (e => - setupMoveUpEvents( - this, - e, - returnFalse, - emptyFunction, - undoBatch(e => click!(e)) - )) - }> +
e.preventDefault()} onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => click!(e)))}>
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 6b4132814..7043edcee 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -156,12 +156,14 @@ export class EditableView extends React.Component { break; case ':': if (this.props.menuCallback) { + e.stopPropagation(); this.props.menuCallback(e.currentTarget.getBoundingClientRect().x, e.currentTarget.getBoundingClientRect().y); break; } default: if (this.props.textCallback?.(e.key)) { + e.stopPropagation(); this._editing = false; this.props.isEditingCallback?.(false); } diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 75e0e7c4c..53c1f1018 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -10,6 +10,7 @@ import { UserOptions } from '../util/GroupManager'; import './FilterPanel.scss'; import { FieldView } from './nodes/FieldView'; import { SearchBox } from './search/SearchBox'; +import { undoable } from '../util/UndoManager'; interface filterProps { rootDoc: Doc; @@ -31,7 +32,7 @@ export class FilterPanel extends React.Component { return targetView?.ComponentView?.annotationKey ?? targetView?.ComponentView?.fieldKey ?? 'data'; } @computed get targetDocChildren() { - return DocListCast(this.targetDoc?.[this.targetDocChildKey] || Doc.ActiveDashboard?.data); + return [...DocListCast(this.targetDoc?.[this.targetDocChildKey] || Doc.ActiveDashboard?.data), ...DocListCast(this.targetDoc[Doc.LayoutFieldKey(this.targetDoc) + '_sidebar'])]; } @computed get allDocs() { @@ -89,6 +90,7 @@ export class FilterPanel extends React.Component { const fieldKey = Doc.LayoutFieldKey(t); const annos = !Field.toString(Doc.LayoutField(t) as Field).includes('CollectionView'); DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc)); + annos && DocListCast(t[fieldKey + '_sidebar']).forEach(newdoc => newarray.push(newdoc)); }); subDocs = newarray; } @@ -204,7 +206,8 @@ export class FilterPanel extends React.Component { .find(filter => filter.split(':')[0] === facetHeader) ?.split(':')[1] ?? '-empty-' } - onKeyDown={e => e.key === 'Enter' && Doc.setDocFilter(this.targetDoc, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match')} + onBlur={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')} + onKeyDown={e => e.key === 'Enter' && undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')(e)} /> ); case 'checkbox': @@ -220,7 +223,7 @@ export class FilterPanel extends React.Component { ?.split(':')[2] === 'check' } type={type} - onChange={e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove')} + onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval, e.target.checked ? 'check' : 'remove'), 'set filter')} /> {facetValue}
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index b9b92dd2b..625bc760d 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -161,12 +161,10 @@ export class KeyManager { case 'delete': case 'backspace': if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { - UndoManager.RunInBatch(() => { - if (LightboxView.LightboxDoc) { - LightboxView.SetLightboxDoc(undefined); - SelectionManager.DeselectAll(); - } else DocumentDecorations.Instance.onCloseClick(true); - }, 'backspace'); + if (LightboxView.LightboxDoc) { + LightboxView.SetLightboxDoc(undefined); + SelectionManager.DeselectAll(); + } else DocumentDecorations.Instance.onCloseClick(true); return { stopPropagation: true, preventDefault: true }; } break; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 853f9cace..50f451c0a 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -915,11 +915,11 @@ export class MainView extends React.Component { return !SelectionManager.Views().some(dv => dv.rootDoc.freeform_snapLines) ? null : (
- {SnappingManager.horizSnapLines().map(l => ( - + {SnappingManager.horizSnapLines().map((l, i) => ( + ))} - {SnappingManager.vertSnapLines().map(l => ( - + {SnappingManager.vertSnapLines().map((l, i) => ( + ))}
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index a37447505..a5c58c9d2 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -14,7 +14,7 @@ import { DocUtils } from '../documents/Documents'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; -import { undoBatch } from '../util/UndoManager'; +import { undoable, undoBatch } from '../util/UndoManager'; import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; import { DocumentView, OpenWhere } from './nodes/DocumentView'; @@ -51,11 +51,11 @@ export class PropertiesButtons extends React.Component<{}, {}> {
e.stopPropagation()} - onClick={undoBatch(() => { + onClick={undoable(() => { if (SelectionManager.Views().length > 1) { SelectionManager.Views().forEach(dv => (onClick ?? onPropToggle)(dv, dv.rootDoc, property)); } else if (targetDoc) (onClick ?? onPropToggle)(undefined, targetDoc, property); - })}> + }, property)}>
{label}
diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx index 46e6fd188..7b21629da 100644 --- a/src/client/views/PropertiesDocBacklinksSelector.tsx +++ b/src/client/views/PropertiesDocBacklinksSelector.tsx @@ -25,7 +25,7 @@ export class PropertiesDocBacklinksSelector extends React.Component { if (!this.props.DocView) return; - col = Doc.IsDocDataProto(col) ? Doc.MakeDelegate(col) : col; + col = Doc.IsDataProto(col) ? Doc.MakeDelegate(col) : col; DocFocusOrOpen(Doc.GetProto(this.props.DocView.props.Document), undefined, col); }; diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 19c138a21..297820e37 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -21,7 +21,7 @@ import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; import { SharingManager } from '../util/SharingManager'; import { Transform } from '../util/Transform'; -import { undoBatch, UndoManager } from '../util/UndoManager'; +import { undoable, undoBatch, UndoManager } from '../util/UndoManager'; import { EditableView } from './EditableView'; import { FilterPanel } from './FilterPanel'; import { Colors } from './global/globalEnums'; @@ -538,6 +538,7 @@ export class PropertiesView extends React.Component { className="inputBox-input" type="text" value={value} + readOnly={true} onChange={e => { setter(e.target.value); }} @@ -656,21 +657,21 @@ export class PropertiesView extends React.Component { return this.inputBoxDuo( 'hgt', this.shapeHgt, - (val: string) => { + undoable((val: string) => { if (!isNaN(Number(val))) { this.shapeHgt = val; } return true; - }, + }, 'set height'), 'H:', 'wid', this.shapeWid, - (val: string) => { + undoable((val: string) => { if (!isNaN(Number(val))) { this.shapeWid = val; } return true; - }, + }, 'set width'), 'W:' ); } @@ -678,21 +679,21 @@ export class PropertiesView extends React.Component { return this.inputBoxDuo( 'Xps', this.shapeXps, - (val: string) => { + undoable((val: string) => { if (val !== '0' && !isNaN(Number(val))) { this.shapeXps = val; } return true; - }, + }, 'set x coord'), 'X:', 'Yps', this.shapeYps, - (val: string) => { + undoable((val: string) => { if (val !== '0' && !isNaN(Number(val))) { this.shapeYps = val; } return true; - }, + }, 'set y coord'), 'Y:' ); } @@ -867,7 +868,7 @@ export class PropertiesView extends React.Component { regInput = (key: string, value: any, setter: (val: string) => {}) => { return (
- setter(e.target.value)} /> + setter(e.target.value)} />
this.upDownButtons('up', key)))}> @@ -1228,8 +1229,7 @@ export class PropertiesView extends React.Component { } }); - @undoBatch - changeFollowBehavior = action((follow: Opt) => this.sourceAnchor && (this.sourceAnchor.followLinkLocation = follow)); + changeFollowBehavior = undoable((loc: Opt) => this.sourceAnchor && (this.sourceAnchor.followLinkLocation = loc), 'change follow behavior'); @undoBatch changeAnimationBehavior = action((behavior: string) => this.sourceAnchor && (this.sourceAnchor.followLinkAnimEffect = behavior)); @@ -1315,6 +1315,7 @@ export class PropertiesView extends React.Component { autoComplete={'off'} id="link_relationship_input" value={StrCast(LinkManager.currentLink?.link_relationship)} + readOnly={true} onKeyDown={this.onRelationshipKey} onBlur={this.onSelectOutRelationship} onChange={e => this.handlelinkRelationshipChange(e.currentTarget.value)} @@ -1332,6 +1333,7 @@ export class PropertiesView extends React.Component { style={{ textAlign: 'left' }} id="link_description_input" value={Field.toString(LinkManager.currentLink?.link_description as any as Field)} + readOnly={true} onKeyDown={this.onDescriptionKey} onBlur={this.onSelectOutDesc} onChange={e => this.handleDescriptionChange(e.currentTarget.value)} @@ -1457,7 +1459,9 @@ export class PropertiesView extends React.Component {
@@ -1567,7 +1571,7 @@ export class PropertiesView extends React.Component {

Zoom %

- +
this.setZoom(String(zoom), 0.1))}> diff --git a/src/client/views/SidebarAnnos.scss b/src/client/views/SidebarAnnos.scss index a0506cb3a..d7de2b641 100644 --- a/src/client/views/SidebarAnnos.scss +++ b/src/client/views/SidebarAnnos.scss @@ -4,22 +4,26 @@ overflow: auto; flex-flow: row; flex-wrap: wrap; - .sidebarAnnos-filterTag, .sidebarAnnos-filterTag-active, - .sidebarAnnos-filterUser, .sidebarAnnos-filterUser-active { + .sidebarAnnos-filterTag, + .sidebarAnnos-filterTag-active, + .sidebarAnnos-filterUser, + .sidebarAnnos-filterUser-active { font-weight: bold; font-size: 10px; padding-left: 5; padding-right: 5; box-shadow: black 1px 1px 3px; border-radius: 5; - margin: 2; + margin: 2; height: 15; background-color: lightgrey; } - .sidebarAnnos-filterUser, .sidebarAnnos-filterUser-active { + .sidebarAnnos-filterUser, + .sidebarAnnos-filterUser-active { border-radius: 15px; } + .sidebarAnnos-filterUser-active, .sidebarAnnos-filterTag-active { background-color: white; } -} \ No newline at end of file +} diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index e12621f35..c9e52a1db 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -49,13 +49,21 @@ export class SidebarAnnos extends React.Component { ); return keys; } + @computed get allHashtags() { + const keys = new Set(); + DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => StrListCast(doc.tags).forEach(tag => keys.add(tag))); + return Array.from(keys.keys()) + .filter(key => key[0]) + .filter(key => !key.startsWith('_') && (key[0] === '#' || key[0] === key[0].toUpperCase())) + .sort(); + } @computed get allUsers() { const keys = new Set(); DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author))); return Array.from(keys.keys()).sort(); } get filtersKey() { - return '_' + this.sidebarKey + '-docFilters'; + return '_' + this.sidebarKey + '_docFilters'; } anchorMenuClick = (anchor: Doc) => { @@ -179,9 +187,9 @@ export class SidebarAnnos extends React.Component { }; render() { const renderTag = (tag: string) => { - const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:check`); + const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`tags:${tag}:check`); return ( -
Doc.setDocFilter(this.props.rootDoc, tag, tag, 'check', true, this.sidebarKey, e.shiftKey)}> +
Doc.setDocFilter(this.props.rootDoc, 'tags', tag, 'check', true, this.sidebarKey, e.shiftKey)}> {tag}
); @@ -216,6 +224,7 @@ export class SidebarAnnos extends React.Component { }}>
e.stopPropagation()}> {this.allUsers.map(renderUsers)} + {this.allHashtags.map(renderTag)} {Array.from(this.allMetadata.keys()) .sort() .map(key => renderMeta(key, this.allMetadata.get(key)))} diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx new file mode 100644 index 000000000..01e184d6b --- /dev/null +++ b/src/client/views/UndoStack.tsx @@ -0,0 +1,30 @@ +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { UndoManager } from '../util/UndoManager'; +import './ScriptingRepl.scss'; + +@observer +export class UndoStack extends React.Component { + render() { + return ( +
+
r?.scroll({ behavior: 'auto', top: r?.scrollHeight + 20 })} style={{ background: UndoManager.batchCounter.get() ? 'yellow' : undefined }}> + {UndoManager.undoStackNames.map((name, i) => ( +
+
{name.replace(/[^\.]*\./, '')}
+
+ ))} + {Array.from(UndoManager.redoStackNames) + .reverse() + .map((name, i) => ( +
+
+ {name.replace(/[^\.]*\./, '')} +
+
+ ))} +
+
+ ); + } +} diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index cce21a3aa..4ae24af60 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -6,7 +6,7 @@ import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; -import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; +import { ImageCast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { inheritParentAcls } from '../../../fields/util'; import { emptyFunction, incrementTitleCopy } from '../../../Utils'; @@ -20,14 +20,14 @@ import { SelectionManager } from '../../util/SelectionManager'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { DashboardView } from '../DashboardView'; import { LightboxView } from '../LightboxView'; +import { OpenWhere, OpenWhereMod } from '../nodes/DocumentView'; +import { OverlayView } from '../OverlayView'; +import { ScriptingRepl } from '../ScriptingRepl'; import './CollectionDockingView.scss'; import { CollectionFreeFormView } from './collectionFreeForm'; import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; import { TabDocView } from './TabDocView'; import React = require('react'); -import { OpenWhere, OpenWhereMod } from '../nodes/DocumentView'; -import { OverlayView } from '../OverlayView'; -import { ScriptingRepl } from '../ScriptingRepl'; const _global = (window /* browser */ || global) /* node */ as any; @observer @@ -85,6 +85,7 @@ export class CollectionDockingView extends CollectionSubView() { tabItemDropped = () => DragManager.CompleteWindowDrag?.(false); tabDragStart = (proxy: any, finishDrag?: (aborted: boolean) => void) => { + this._flush = this._flush ?? UndoManager.StartBatch('tab move'); const dashDoc = proxy?._contentItem?.tab?.DashDoc as Doc; dashDoc && (DragManager.DocDragData = new DragManager.DocumentDragData([proxy._contentItem.tab.DashDoc])); DragManager.CompleteWindowDrag = (aborted: boolean) => { @@ -92,12 +93,12 @@ export class CollectionDockingView extends CollectionSubView() { proxy._dragListener.AbortDrag(); if (this._flush) { this._flush.cancel(); // cancel the undo change being logged - this._flush = undefined; this.setupGoldenLayout(); // restore golden layout to where it was before the drag (this is a no-op when using StartOtherDrag because the proxy dragged item was never in the golden layout) } DragManager.CompleteWindowDrag = undefined; } finishDrag?.(aborted); + setTimeout(this.endUndoBatch, 100); }; }; @undoBatch @@ -180,7 +181,6 @@ export class CollectionDockingView extends CollectionSubView() { // // Creates a split on any side of the docking view based on the passed input pullSide and then adds the Document to the requested side // - @undoBatch @action public static AddSplit(document: Doc, pullSide: OpenWhereMod, stack?: any, panelName?: string, keyValue?: boolean) { if (document?._type_collection === CollectionViewType.Docking) return DashboardView.openDashboard(document); @@ -195,6 +195,8 @@ export class CollectionDockingView extends CollectionSubView() { if (!instance) return false; const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName, undefined, keyValue); + CollectionDockingView.Instance._flush = CollectionDockingView.Instance._flush ?? UndoManager.StartBatch('Add Split'); + setTimeout(CollectionDockingView.Instance.endUndoBatch, 100); if (!pullSide && stack) { stack.addChild(docContentConfig, undefined); setTimeout(() => stack.setActiveContentItem(stack.contentItems[stack.contentItems.length - 1])); @@ -370,16 +372,30 @@ export class CollectionDockingView extends CollectionSubView() { !LightboxView.LightboxDoc && cur && this._goldenLayout?.updateSize(cur.getBoundingClientRect().width, cur.getBoundingClientRect().height); }; + endUndoBatch = () => { + const json = JSON.stringify(this._goldenLayout.toConfig()); + const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g); + const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')); + const docs = !docids + ? [] + : docids + .map(id => DocServer.GetCachedRefField(id)) + .filter(f => f) + .map(f => f as Doc); + const changesMade = this.props.Document.dockingConfig !== json; + if (changesMade) { + this.props.Document.dockingConfig = json; + this.props.Document.data = new List(docs); + } + this._flush?.end(); + this._flush = undefined; + }; + @action onPointerUp = (e: MouseEvent): void => { window.removeEventListener('pointerup', this.onPointerUp); - const flush = this._flush; - this._flush = undefined; - if (flush) { - DragManager.CompleteWindowDrag = undefined; - if (!this.stateChanged()) flush.cancel(); - else flush.end(); - } + DragManager.CompleteWindowDrag = undefined; + setTimeout(this.endUndoBatch, 100); }; @action @@ -393,10 +409,8 @@ export class CollectionDockingView extends CollectionSubView() { window.addEventListener('mouseup', this.onPointerUp); if (!htmlTarget.closest('*.lm_content') && (htmlTarget.closest('*.lm_tab') || htmlTarget.closest('*.lm_stack'))) { const className = typeof htmlTarget.className === 'string' ? htmlTarget.className : ''; - if (!className.includes('lm_close') && !className.includes('lm_maximise')) { - this._flush = UndoManager.StartBatch('golden layout edit'); - DocServer.UPDATE_SERVER_CACHE(); - } + if (className.includes('lm_maximise')) this._flush = UndoManager.StartBatch('tab maximize'); + else if (!className.includes('lm_close')) DocServer.UPDATE_SERVER_CACHE(); } } if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { @@ -452,25 +466,12 @@ export class CollectionDockingView extends CollectionSubView() { stateChanged = () => { this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig()); const json = JSON.stringify(this._goldenLayout.toConfig()); - const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g); - const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')); - const docs = !docids - ? [] - : docids - .map(id => DocServer.GetCachedRefField(id)) - .filter(f => f) - .map(f => f as Doc); const changesMade = this.props.Document.dockingConfig !== json; - if (changesMade && !this._flush) { - UndoManager.RunInBatch(() => { - this.props.Document.dockingConfig = json; - this.props.Document.data = new List(docs); - }, 'state changed'); - } return changesMade; }; tabDestroyed = (tab: any) => { + this._flush = this._flush ?? UndoManager.StartBatch('tab movement'); if (tab.DashDoc && ![DocumentType.KVP, DocumentType.PRES].includes(tab.DashDoc?.type)) { Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc); Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true); diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx index 63becac1e..2f28ecd00 100644 --- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx +++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx @@ -121,7 +121,8 @@ export class CollectionNoteTakingViewColumn extends React.Component SnappingManager.GetIsDragging() && (this._background = '#b4b4b4'); @action pointerLeave = () => (this._background = 'inherit'); - textCallback = (char: string) => this.addNewTextDoc('-typed text-', false, true); + @undoBatch + addTextNote = (char: string) => this.addNewTextDoc('-typed text-', false, true); // addNewTextDoc is called when a user starts typing in a column to create a new node @action @@ -272,7 +273,7 @@ export class CollectionNoteTakingViewColumn extends React.Component
- +
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 6b4c8a3e9..b131d38d8 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -119,7 +119,8 @@ export class CollectionStackedTimeline extends CollectionSubView scriptContext.clickAnchor(this, clientX))`, { + // setTimeout is a hack to run script in its own properly named undo group (instead of being part of the generic onClick) self: Doc.name, scriptContext: 'any', clientX: 'number', diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 243550c0b..6be9cb72d 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -128,7 +128,7 @@ export class CollectionStackingViewFieldColumn extends React.Component SnappingManager.GetIsDragging() && (this._background = '#b4b4b4'); @action pointerLeave = () => (this._background = 'inherit'); - textCallback = (char: string) => this.addNewTextDoc('-typed text-', false, true); + @undoBatch typedNote = (char: string) => this.addNewTextDoc('-typed text-', false, true); @action addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => { @@ -363,7 +363,7 @@ export class CollectionStackingViewFieldColumn extends React.Component} menuCallback={this.menuCallback} diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index bcda13c8b..cfe78afa1 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -5,7 +5,6 @@ import { AclPrivate, Doc, DocListCast, Field, Opt, StrListCast } from '../../../ import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; -import { ScriptField } from '../../../fields/ScriptField'; import { Cast, ScriptCast, StrCast } from '../../../fields/Types'; import { WebField } from '../../../fields/URLField'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; @@ -65,11 +64,7 @@ export function CollectionSubView(moreProps?: X) { // to its children which may be templates. // If 'annotationField' is specified, then all children exist on that field of the extension document, otherwise, they exist directly on the data document under 'fieldKey' @computed get dataField() { - if (this.layoutDoc[this.props.fieldKey]) return this.layoutDoc[this.props.fieldKey]; - // sets the dataDoc's data field to an empty list if the data field is undefined - prevents issues with addonly - // setTimeout changes it outside of the @computed section - !this.dataDoc[this.props.fieldKey] && setTimeout(() => !this.dataDoc[this.props.fieldKey] && (this.dataDoc[this.props.fieldKey] = new List())); - return this.dataDoc[this.props.fieldKey]; + return this.layoutDoc[this.props.fieldKey]; } get childLayoutPairs(): { layout: Doc; data: Doc }[] { @@ -130,8 +125,9 @@ export function CollectionSubView(moreProps?: X) { const fieldKey = Doc.LayoutFieldKey(d); const annos = !Field.toString(Doc.LayoutField(d) as Field).includes(CollectionView.name); const data = d[annos ? fieldKey + '_annotations' : fieldKey]; - if (data !== undefined) { - let subDocs = DocListCast(data); + const side = annos && d[fieldKey + '_sidebar']; + if (data !== undefined || side !== undefined) { + let subDocs = [...DocListCast(data), ...DocListCast(side)]; if (subDocs.length > 0) { let newarray: Doc[] = []; notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, childDocFilters, docRangeFilters, d).length); @@ -142,6 +138,7 @@ export function CollectionSubView(moreProps?: X) { const annos = !Field.toString(Doc.LayoutField(t) as Field).includes(CollectionView.name); notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], childDocFilters, docRangeFilters, d).length)); DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc)); + annos && DocListCast(t[fieldKey + '_sidebar']).forEach(newdoc => newarray.push(newdoc)); }); subDocs = newarray; } diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 7e88959a4..33fa434e1 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -13,14 +13,13 @@ import { listSpec } from '../../../fields/Schema'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types'; import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../../Utils'; import { DocServer } from '../../DocServer'; -import { DocUtils } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType } from '../../util/DragManager'; import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; -import { undoBatch, UndoManager } from '../../util/UndoManager'; +import { undoable, UndoManager } from '../../util/UndoManager'; import { DashboardView } from '../DashboardView'; import { Colors, Shadows } from '../global/globalEnums'; import { LightboxView } from '../LightboxView'; @@ -116,15 +115,13 @@ export class TabDocView extends React.Component { titleEle.size = StrCast(doc.title).length + 3; titleEle.value = doc.title; - titleEle.onkeydown = (e: KeyboardEvent) => { - e.stopPropagation(); - }; - titleEle.onchange = undoBatch( - action((e: any) => { + titleEle.onkeydown = (e: KeyboardEvent) => e.stopPropagation(); + titleEle.onchange = (e: any) => { + undoable(() => { titleEle.size = e.currentTarget.value.length + 3; Doc.GetProto(doc).title = e.currentTarget.value; - }) - ); + }, 'edit tab title')(); + }; if (tab.element[0].children[1].children.length === 1) { iconWrap.className = 'lm_iconWrap lm_moreInfo'; @@ -198,7 +195,9 @@ export class TabDocView extends React.Component { action(selected => { if (selected) this._activated = true; const toggle = tab.element[0].children[2].children[0] as HTMLInputElement; - selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && UndoManager.RunInBatch(() => tab.header.parent.setActiveContentItem(tab.contentItem), 'tab switch'); + if (selected && tab.contentItem !== tab.header.parent.getActiveContentItem()) { + undoable(() => tab.header.parent.setActiveContentItem(tab.contentItem), 'tab switch')(); + } toggle.style.fontWeight = selected ? 'bold' : ''; // toggle.style.textTransform = selected ? "uppercase" : ""; }), @@ -234,7 +233,7 @@ export class TabDocView extends React.Component { public static PinDoc(docs: Doc | Doc[], pinProps: PinProps) { const docList = docs instanceof Doc ? [docs] : docs; - const batch = UndoManager.StartBatch('pinning doc'); + const batch = UndoManager.StartBatch('Pin doc to pres trail'); const curPres = Doc.ActivePresentation ?? Doc.MakeCopy(Doc.UserDoc().emptyTrail as Doc, true); if (!Doc.ActivePresentation) { diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 037148bb9..f56eaee07 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -102,7 +102,7 @@ export class TreeView extends React.Component { private _treedropDisposer?: DragManager.DragDropDisposer; get treeViewOpenIsTransient() { - return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsDocDataProto(this.doc); + return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsDataProto(this.doc); } set treeViewOpen(c: boolean) { if (this.treeViewOpenIsTransient) this._transientOpenState = c; @@ -221,7 +221,7 @@ export class TreeView extends React.Component { } else { // choose an appropriate embedding or make one. --- choose the first embedding that (1) user owns, (2) has no context field ... otherwise make a new embedding const bestEmbedding = - docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsDocDataProto(docView.props.Document) + docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsDataProto(docView.props.Document) ? docView.props.Document : DocListCast(this.props.document.proto_embeddings).find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail); const nextBestEmbedding = DocListCast(this.props.document.proto_embeddings).find(doc => doc.author === Doc.CurrentUserEmail); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 5ac444147..95046661e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -483,7 +483,6 @@ export class CollectionFreeFormView extends CollectionSubView pair.layout); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 47d7801e6..641088675 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -102,7 +102,6 @@ export class MarqueeView extends React.Component { //make textbox and add it to this collection @@ -111,7 +110,6 @@ export class MarqueeView extends React.Component this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', data_useCors: true }), OpenWhere.addRight)); - cm.displayMenu(this._downX, this._downY, undefined, true); e.stopPropagation(); } else if (e.key === 'u' && this.props.ungroup) { @@ -173,7 +171,7 @@ export class MarqueeView extends React.Component { const selected = this.marqueeSelect(false); @@ -508,8 +507,7 @@ export class MarqueeView extends React.Component { + summary = action((e: KeyboardEvent | React.PointerEvent | undefined) => { const selected = this.marqueeSelect(false).map(d => { this.props.removeDocument?.(d); d.x = NumCast(d.x) - this.Bounds.left; @@ -534,19 +532,10 @@ export class MarqueeView extends React.Component { - const newCollection = this.getCollection([], undefined, undefined); - this.props.addDocument?.(newCollection); - MarqueeOptionsMenu.Instance.fadeOut(true); - this.hideMarquee(); - setTimeout(() => this.props.selectDocuments([newCollection])); - }; - - @undoBatch - marqueeCommand = action((e: KeyboardEvent) => { + marqueeCommand = (e: KeyboardEvent) => { if (this._commandExecuted || (e as any).propagationIsStopped) { return; } @@ -557,7 +546,7 @@ export class MarqueeView extends React.Component { + setColumnSort = (field: string | undefined, desc: boolean = false) => { this.layoutDoc.sortField = field; this.layoutDoc.sortDesc = desc; }; @@ -484,24 +486,11 @@ export class CollectionSchemaView extends CollectionSubView() { return false; }; - @action - addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => { - if (!value && !forceEmptyNote) return false; - const newDoc = Docs.Create.TextDocument(value, { title: value, _layout_autoHeight: true }); - FormattedTextBox.SelectOnLoad = newDoc[Id]; - FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' '; - return this.addRow(newDoc) || false; - }; - menuCallback = (x: number, y: number) => { ContextMenu.Instance.clearItems(); - DocUtils.addDocumentCreatorMenuItems(doc => this.addRow(doc), this.addRow, x, y, true); + DocUtils.addDocumentCreatorMenuItems(this.addRow, this.addRow, x, y, true); - ContextMenu.Instance.setDefaultItem('::', (name: string): void => { - Doc.GetProto(this.props.Document)[name] = ''; - this.addRow(Docs.Create.TextDocument('', { title: name, _layout_autoHeight: true })); - }); ContextMenu.Instance.displayMenu(x, y, undefined, true); }; @@ -866,7 +855,7 @@ export class CollectionSchemaView extends CollectionSubView() { columnWidths={this.displayColumnWidths} sortField={this.sortField} sortDesc={this.sortDesc} - setSort={this.setSort} + setSort={this.setColumnSort} rowHeight={this.rowHeightFunc} removeColumn={this.removeColumn} resizeColumn={this.startResize} @@ -879,7 +868,14 @@ export class CollectionSchemaView extends CollectionSubView() { {this._columnMenuIndex !== undefined && this.renderColumnMenu} {this._filterColumnIndex !== undefined && this.renderFilterMenu} (this._tableContentRef = ref)} /> - + (value ? this.addRow(Docs.Create.TextDocument(value, { title: value, _layout_autoHeight: true })) : false), 'add text doc')} + placeholder={"Type ':' for commands"} + contents={'+ New Node'} + menuCallback={this.menuCallback} + height={CollectionSchemaView._newNodeInputHeight} + />
{this.previewWidth > 0 &&
} {this.previewWidth > 0 && ( diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx index 7da3c042c..46c2f622b 100644 --- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx +++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx @@ -5,8 +5,6 @@ import { observer } from 'mobx-react'; import { emptyFunction, setupMoveUpEvents } from '../../../../Utils'; import { Colors } from '../../global/globalEnums'; import './CollectionSchemaView.scss'; -import { SnappingManager } from '../../../util/SnappingManager'; -import { DragManager } from '../../../util/DragManager'; export interface SchemaColumnHeaderProps { columnKeys: string[]; diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 45bfe4f77..978b65053 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -7,7 +7,7 @@ import { Doc } from '../../../../fields/Doc'; import { BoolCast } from '../../../../fields/Types'; import { DragManager } from '../../../util/DragManager'; import { SnappingManager } from '../../../util/SnappingManager'; -import { undoBatch } from '../../../util/UndoManager'; +import { undoable, undoBatch } from '../../../util/UndoManager'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; import { OpenWhere } from '../../nodes/DocumentView'; @@ -111,18 +111,18 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { }}>
{ + onPointerDown={undoable(e => { e.stopPropagation(); this.props.removeDocument?.(this.rootDoc); - })}> + }, 'Delete Row')}>
{ + onPointerDown={undoable(e => { e.stopPropagation(); this.props.addDocTab(this.rootDoc, OpenWhere.addRight); - }}> + }, 'Open Doc on Right')}>
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 42bf32475..6d5ef9df6 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -122,7 +122,7 @@ export class SchemaTableCell extends React.Component { const ret = KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value); this.props.finishEdit?.(); return ret; - })} + }, 'edit schema cell')} />
); diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index 3f6369898..65d13a6c3 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -49,7 +49,7 @@ export class LinkMenu extends React.Component { )); - return linkItems.length ? linkItems : this.props.style ? [<>] : [

No links have been created yet. Drag the linking button onto another document to create a link.

]; + return linkItems.length ? linkItems : this.props.style ? [] : [

No links have been created yet. Drag the linking button onto another document to create a link.

]; }; render() { diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 8e83cf121..410e0bbdc 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -447,7 +447,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent this.props.ScreenToLocalTransform().translate(0, -AudioBox.topControlsHeight); - setPlayheadTime = (time: number) => (this._ele!.currentTime = this.layoutDoc._layout_currentTimecode = time); + setPlayheadTime = (time: number) => (this._ele!.currentTime /*= this.layoutDoc._layout_currentTimecode*/ = time); playing = () => this.mediaState === media_state.Playing; diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index d5ca30957..bd1952ecb 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -9,7 +9,7 @@ import { DocUtils } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { Hypothesis } from '../../util/HypothesisUtils'; import { LinkManager } from '../../util/LinkManager'; -import { undoBatch, UndoManager } from '../../util/UndoManager'; +import { undoable, undoBatch, UndoManager } from '../../util/UndoManager'; import './DocumentLinksButton.scss'; import { DocumentView } from './DocumentView'; import { LinkDescriptionPopup } from './LinkDescriptionPopup'; @@ -78,7 +78,6 @@ export class DocumentLinksButton extends React.Component { setupMoveUpEvents( this, @@ -123,17 +122,14 @@ export class DocumentLinksButton extends React.Component { - DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View); - }) - ) + action(e => DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)) ); }; - public static finishLinkClick = undoBatch( - action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView, pinProps?: PinProps) => { - if (startLink === endLink) { + @undoBatch + public static finishLinkClick(screenX: number, screenY: number, startLink: Doc | undefined, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView, pinProps?: PinProps) { + runInAction(() => { + if (startLink === endLink || !startLink) { DocumentLinksButton.StartLink = undefined; DocumentLinksButton.StartLinkView = undefined; DocumentLinksButton.AnnotationId = undefined; @@ -185,8 +181,8 @@ export class DocumentLinksButton extends React.Component (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'on click')); + clickFunc = () => (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'click ' + this.rootDoc.title)); } else { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) { @@ -490,7 +491,7 @@ export class DocumentViewInternal extends DocComponent { - this._longPressSelector = setTimeout(() => DocumentView.LongPress && this.props.select(false), 1000); + this._longPressSelector = setTimeout(() => { + if (DocumentView.LongPress) { + if (this.rootDoc.dontUndo) { + OverlayView.Instance.addWindow(, { x: 300, y: 100, width: 200, height: 200, title: 'Undo Stack' }); + } else { + this.props.select(false); + } + } + }, 1000); if (!GestureOverlay.DownDocView) GestureOverlay.DownDocView = this.props.DocumentView(); this._downX = e.clientX; @@ -557,7 +566,7 @@ export class DocumentViewInternal extends DocComponent { this._markerTargetDoc = linkTarget; this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget; } - this._toolTipText = 'link to ' + this._targetDoc?.title; if (LinkDocPreview.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink(); } }) @@ -296,7 +295,7 @@ export class LinkDocPreview extends React.Component { className="linkDocPreview" ref={this._linkDocRef} onPointerDown={this.followLinkPointerDown} - style={{ display: !this._toolTipText ? 'none' : undefined, left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}> + style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}> {this.docPreview}
); diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 57aa852ac..b07cf7e00 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -16,7 +16,7 @@ import { CollectionViewType, DocumentType } from '../../../documents/DocumentTyp import { LinkManager } from '../../../util/LinkManager'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; import { SelectionManager } from '../../../util/SelectionManager'; -import { undoBatch, UndoManager } from '../../../util/UndoManager'; +import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager'; import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; import { ContextMenu } from '../../ContextMenu'; import { DocComponent } from '../../DocComponent'; @@ -129,29 +129,27 @@ export class FontIconBox extends DocComponent() { * Number button */ @computed get numberSliderButton() { - const numScript = ScriptCast(this.rootDoc.script); - const setValue = (value: number) => UndoManager.RunInBatch(() => numScript?.script.run({ self: this.rootDoc, value, _readOnly_: false }), 'set num value'); - + const numScript = (value?: number) => ScriptCast(this.rootDoc.script).script.run({ self: this.rootDoc, value, _readOnly_: value === undefined }); // Script for checking the outcome of the toggle - const checkResult = Number(numScript?.script.run({ self: this.rootDoc, value: 0, _readOnly_: true }).result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)); - + const checkResult = Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)); const label = !FontIconBox.GetShowLabels() ? null :
{this.label}
; const dropdown = (
e.stopPropagation()}> (this._batch = UndoManager.StartBatch('presDuration'))} + onPointerDown={() => (this._batch = UndoManager.StartBatch('num slider changing'))} onPointerUp={() => this._batch?.end()} - onChange={e => { + onChange={undoable(e => { e.stopPropagation(); - setValue(Number(e.target.value)); - }} + numScript(Number(e.target.value)); + }, 'set num value')} />
); @@ -174,20 +172,13 @@ export class FontIconBox extends DocComponent() { * Number button */ @computed get numberDropdownButton() { - const numScript = ScriptCast(this.rootDoc.script); - const setValue = (value: number) => UndoManager.RunInBatch(() => numScript?.script.run({ self: this.rootDoc, value, _readOnly_: false }), 'set num value'); - - // Script for checking the outcome of the toggle - const checkResult = Number(numScript?.script.run({ self: this.rootDoc, value: 0, _readOnly_: true }).result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)); + const numScript = (value?: number) => ScriptCast(this.rootDoc.script)?.script.run({ self: this.rootDoc, value, _readOnly_: value === undefined }); - const label = !FontIconBox.GetShowLabels() ? null :
{this.label}
; + const checkResult = Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)); const items: number[] = []; - for (let i = 0; i < 100; i++) { - if (i % 2 === 0) { - items.push(i); - } - } + for (let i = 0; i < 100; i += 2) items.push(i); + const list = items.map(value => { return (
() { style={{ backgroundColor: value.toString() === checkResult ? Colors.LIGHT_BLUE : undefined, }} - onClick={() => setValue(value)}> + onClick={undoable(value => numScript(value), `${this.rootDoc.title} button set from list`)}> {value}
); }); return (
-
setValue(Number(checkResult) - 1))}> - +
numScript(Number(checkResult) - 1), `${this.rootDoc.title} decrement value`)}> +
() { this.noTooltip = this.rootDoc.dropDownOpen; Doc.UnBrushAllDocs(); })}> - setValue(Number(e.target.value))))} /> + numScript(Number(e.target.value)), `${this.rootDoc.title} button set value`)} />
-
setValue(Number(checkResult) + 1))}> +
numScript(Number(checkResult) + 1), `${this.rootDoc.title} increment value`)}>
@@ -322,12 +313,12 @@ export class FontIconBox extends DocComponent() { .map(value => (
script.script.run({ self: this.rootDoc, value }))}> + onClick={undoable(() => script.script.run({ self: this.rootDoc, value }), value)}> {value[0].toUpperCase() + value.slice(1)}
)); @@ -640,29 +631,26 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { ScriptingGlobals.add(function showFreeform(attr: 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) { const selected = SelectionManager.Docs().lastElement(); // prettier-ignore - const map: Map<'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll', { undo: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([ + const map: Map<'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([ ['grid', { - undo: false, checkResult: (doc:Doc) => doc._freeform_backgroundGrid, setDoc: (doc:Doc) => doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid, }], ['snaplines', { - undo: false, checkResult: (doc:Doc) => doc._freeform_snapLines, setDoc: (doc:Doc) => doc._freeform_snapLines = !doc._freeform_snapLines, }], ['viewAll', { - undo: false, checkResult: (doc:Doc) => doc._freeform_fitContentsToBox, setDoc: (doc:Doc) => doc._freeform_fitContentsToBox = !doc._freeform_fitContentsToBox, }], ['clusters', { - undo: false, + waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire checkResult: (doc:Doc) => doc._freeform_useClusters, setDoc: (doc:Doc) => doc._freeform_useClusters = !doc._freeform_useClusters, }], ['arrange', { - undo: true, + waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire checkResult: (doc:Doc) => doc._autoArrange, setDoc: (doc:Doc) => doc._autoArrange = !doc._autoArrange, }], @@ -671,7 +659,7 @@ ScriptingGlobals.add(function showFreeform(attr: 'grid' | 'snaplines' | 'cluster if (checkResult) { return map.get(attr)?.checkResult(selected) ? Colors.MEDIUM_BLUE : 'transparent'; } - const batch = map.get(attr)?.undo ? UndoManager.StartBatch('set feature') : { end: () => {} }; + const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} }; SelectionManager.Docs().map(dv => map.get(attr)?.setDoc(dv)); setTimeout(() => batch.end(), 100); }); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 9c06aa7d8..e85835002 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -301,11 +301,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent (json?.indexOf('"storedMarks"') === -1 ? json?.replace(/"selection":.*/, '') : json?.replace(/"selection":"\"storedMarks\""/, '"storedMarks"')); @@ -320,29 +320,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent(Array.from(new Set(accumTags))) : undefined; let unchanged = true; - if (this._applyingChange !== this.fieldKey && removeSelection(json) !== removeSelection(curProto?.Data)) { + if (this._applyingChange !== this.fieldKey && removeSelection(newJson) !== removeSelection(prevData?.Data)) { this._applyingChange = this.fieldKey; - const textChange = curText !== Cast(dataDoc[this.fieldKey], RichTextField)?.Text; + const textChange = newText !== prevData?.Text; textChange && (dataDoc[this.fieldKey + '_modificationDate'] = new DateField(new Date(Date.now()))); - if ((!curTemp && !curProto) || curText || json.includes('dash')) { + if ((!prevData && !protoData) || newText || (!newText && !protoData)) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended) - if (removeSelection(json) !== removeSelection(curLayout?.Data)) { + if (removeSelection(newJson) !== removeSelection(prevLayoutData?.Data)) { const numstring = NumCast(dataDoc[this.fieldKey], null); - if (numstring !== undefined) { - dataDoc[this.fieldKey] = Number(curText); - } else { - dataDoc[this.fieldKey] = new RichTextField(json, curText); - } + dataDoc[this.fieldKey] = numstring !== undefined ? Number(newText) : new RichTextField(newJson, newText); dataDoc[this.fieldKey + '_noTemplate'] = true; //(curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited - textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText }); + textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: newText }); unchanged = false; } } else { // if we've deleted all the text in a note driven by a template, then restore the template data dataDoc[this.fieldKey] = undefined; - this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse((curProto || curTemp).Data))); + this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse((protoData || prevData).Data))); dataDoc[this.fieldKey + '_noTemplate'] = undefined; // mark the data field as not being split from any template it might have - ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText }); + ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: newText }); unchanged = false; } this._applyingChange = ''; @@ -667,7 +663,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - const batch = UndoManager.StartBatch('sidebar'); + const batch = UndoManager.StartBatch('toggle sidebar'); setupMoveUpEvents( this, e, @@ -694,13 +690,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { + const batch = UndoManager.StartBatch('delete link'); LinkManager.Instance.deleteLink(LinkManager.Links(anchor)[0]); // const docAnnotations = DocListCast(this.props.dataDoc[this.fieldKey]); // this.props.dataDoc[this.fieldKey] = new List(docAnnotations.filter(a => a !== this.annoTextRegion)); // AnchorMenu.Instance.fadeOut(true); this.props.select(false); + setTimeout(batch.end); // wait for reaction to remove link from document }; @undoBatch @@ -733,17 +730,22 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent h); const anchorDoc = Array.from(hrefs).lastElement().replace(Doc.localServerPath(), '').split('?')[0]; + const deleteMarkups = undoBatch(() => { + const sel = editor.state.selection; + editor.dispatch(editor.state.tr.removeMark(sel.from, sel.to, editor.state.schema.marks.linkAnchor)); + }); e.persist(); anchorDoc && DocServer.GetRefField(anchorDoc).then( action(anchor => { + anchor && SelectionManager.SelectSchemaViewDoc(anchor as Doc); AnchorMenu.Instance.Status = 'annotation'; - AnchorMenu.Instance.Delete = () => this.deleteAnnotation(anchor as Doc); + AnchorMenu.Instance.Delete = !anchor && editor.state.selection.empty ? returnFalse : !anchor ? deleteMarkups : () => this.deleteAnnotation(anchor as Doc); AnchorMenu.Instance.Pinned = false; - AnchorMenu.Instance.PinToPres = () => this.pinToPres(anchor as Doc); - AnchorMenu.Instance.MakeTargetToggle = () => this.makeTargetToggle(anchor as Doc); - AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(anchor as Doc); - AnchorMenu.Instance.IsTargetToggler = () => this.isTargetToggler(anchor as Doc); + AnchorMenu.Instance.PinToPres = !anchor ? returnFalse : () => this.pinToPres(anchor as Doc); + AnchorMenu.Instance.MakeTargetToggle = !anchor ? returnFalse : () => this.makeTargetToggle(anchor as Doc); + AnchorMenu.Instance.ShowTargetTrail = !anchor ? returnFalse : () => this.showTargetTrail(anchor as Doc); + AnchorMenu.Instance.IsTargetToggler = !anchor ? returnFalse : () => this.isTargetToggler(anchor as Doc); AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true); }) ); @@ -1002,7 +1004,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(from), tr.doc.resolve(to)))), 250); this._editorView.state.storedMarks = [ ...(this._editorView.state.storedMarks ?? []), @@ -1630,7 +1631,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { //applyDevTools.applyDevTools(this._editorView); this.ProseRef?.children[0] === e.nativeEvent.target && this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props); - this.startUndoTypingBatch(); + e.stopPropagation(); }; onClick = (e: React.MouseEvent): void => { @@ -1705,13 +1706,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent() { min={min} max={max} value={value} + readOnly={true} style={{ marginLeft: hmargin, marginRight: hmargin, width: `calc(100% - ${2 * (hmargin ?? 0)}px)` }} className={`toolbar-slider ${active ? '' : 'none'}`} onPointerDown={e => { @@ -1501,7 +1502,7 @@ export class PresBox extends ViewBoxBaseComponent() {
Slide Duration
- e.stopPropagation()} onChange={e => this.updateDurationTime(e.target.value)} /> s + e.stopPropagation()} onChange={e => this.updateDurationTime(e.target.value)} /> s
this.updateDurationTime(String(duration), 1000)}> @@ -1654,7 +1655,7 @@ export class PresBox extends ViewBoxBaseComponent() {
Zoom (% screen filled)
- this.updateZoom(e.target.value)} />% + this.updateZoom(e.target.value)} />%
this.updateZoom(String(zoom), 0.1)}> @@ -1669,7 +1670,7 @@ export class PresBox extends ViewBoxBaseComponent() {
Transition Time
- e.stopPropagation()} onChange={action(e => this.updateTransitionTime(e.target.value))} /> s + e.stopPropagation()} onChange={action(e => this.updateTransitionTime(e.target.value))} /> s
this.updateTransitionTime(String(transitionSpeed), 1000)}> @@ -1756,6 +1757,7 @@ export class PresBox extends ViewBoxBaseComponent() { className="presBox-input" style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }} type="number" + readOnly={true} value={NumCast(activeItem.presStartTime).toFixed(2)} onKeyDown={e => e.stopPropagation()} onChange={action((e: React.ChangeEvent) => { @@ -1782,6 +1784,7 @@ export class PresBox extends ViewBoxBaseComponent() { onKeyDown={e => e.stopPropagation()} style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }} type="number" + readOnly={true} value={NumCast(activeItem.presEndTime).toFixed(2)} onChange={action((e: React.ChangeEvent) => { activeItem.presEndTime = Number(e.target.value); diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 4eb6aee25..2279ffe84 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -1,6 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; -import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; @@ -13,7 +13,7 @@ import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { SettingsManager } from '../../../util/SettingsManager'; import { Transform } from '../../../util/Transform'; -import { undoBatch } from '../../../util/UndoManager'; +import { undoable, undoBatch } from '../../../util/UndoManager'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { EditableView } from '../../EditableView'; import { Colors } from '../../global/globalEnums'; @@ -263,16 +263,15 @@ export class PresElementBox extends ViewBoxBaseComponent() { } }; - @undoBatch - removeItem = action((e: React.MouseEvent) => { + removePresentationItem = undoable((e: React.MouseEvent) => { e.stopPropagation(); if (this.presBox && this.indexInPres < (this.presBoxView?.itemIndex || 0)) { - this.presBox.itemIndex = (this.presBoxView?.itemIndex || 0) - 1; + runInAction(() => (this.presBox!.itemIndex = (this.presBoxView?.itemIndex || 0) - 1)); } this.props.removeDocument?.(this.rootDoc); this.presBoxView?.removeFromSelectedArray(this.rootDoc); this.removeAllRecordingInOverlay(); - }); + }, 'Remove doc from pres trail'); // set the value/title of the individual pres element @undoBatch @@ -476,7 +475,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { ); items.push( Remove from presentation
}> -
+
e.stopPropagation()} />
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index d6dddf71a..5480600b0 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -395,22 +395,25 @@ export class AnchorMenu extends AntimodeMenu { ) : ( <> Remove Link Anchor
}> - Pin to Presentation
}> - Show Linked Trail
}> - make target visibility toggle on click
}> - diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 1c1b41f73..3479cd20f 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -141,6 +141,8 @@ export class SearchBox extends ViewBoxBaseComponent() { const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView'); const data = d[annos ? fieldKey + '_annotations' : fieldKey]; data && newarray.push(...DocListCast(data)); + const sidebar = d[fieldKey + '_sidebar']; + sidebar && newarray.push(...DocListCast(sidebar)); func(depth, d); }); docs = newarray; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 5312da009..d8447deb6 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -236,7 +236,7 @@ export class Doc extends RefField { if ( doc && Doc.MyFileOrphans instanceof Doc && - Doc.IsDocDataProto(doc) && + Doc.IsDataProto(doc) && !Doc.IsSystem(doc) && ![DocumentType.CONFIG, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(doc.type as any) && !doc.isFolder && @@ -510,7 +510,7 @@ export namespace Doc { export function GetT(doc: Doc, key: string, ctor: ToConstructor, ignoreProto: boolean = false): FieldResult { return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult; } - export function IsDocDataProto(doc: Doc) { + export function IsDataProto(doc: Doc) { return GetT(doc, 'isDataDoc', 'boolean', true); } export function IsBaseProto(doc: Doc) { @@ -981,7 +981,7 @@ export namespace Doc { export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string, retitle = false): Doc { const copy = new Doc(copyProtoId, true); updateCachedAcls(copy); - const exclude = [...Cast(doc.cloneFieldFilter, listSpec('string'), []), 'dragFactory_count', 'cloneFieldFilter']; + const exclude = [...StrListCast(doc.cloneFieldFilter), 'dragFactory_count', 'cloneFieldFilter']; Object.keys(doc).forEach(key => { if (exclude.includes(key)) return; const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); @@ -1433,7 +1433,7 @@ export namespace Doc { export function setDocFilter(container: Opt, key: string, value: any, modifiers: 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldPrefix?: string, append: boolean = true) { if (!container) return; const filterField = '_' + (fieldPrefix ? fieldPrefix + '_' : '') + 'docFilters'; - const docFilters = Cast(container[filterField], listSpec('string'), []); + const docFilters = StrListCast(container[filterField]); runInAction(() => { for (let i = 0; i < docFilters.length; i++) { const fields = docFilters[i].split(':'); // split key:value:modifier @@ -1745,6 +1745,16 @@ ScriptingGlobals.add(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); }); + +export function ShowUndoStack() { + SelectionManager.DeselectAll(); + var buffer = ''; + UndoManager.undoStack.forEach((batch, i) => { + buffer += 'Batch => ' + UndoManager.undoStackNames[i] + '\n'; + ///batch.forEach(undo => (buffer += ' ' + undo.prop + '\n')); + }); + alert(buffer); +} ScriptingGlobals.add(function redo() { SelectionManager.DeselectAll(); return UndoManager.Redo(); diff --git a/src/fields/util.ts b/src/fields/util.ts index f4fd3200c..a2b445d6c 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -65,6 +65,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number if (value instanceof RefField) { value = new ProxyField(value); } + if (value instanceof ObjectField) { if (value[Parent] && value[Parent] !== receiver && !(value instanceof PrefetchProxy)) { throw new Error("Can't put the same object in multiple documents at the same time"); @@ -102,20 +103,24 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue); } !receiver[Initializing] && + !receiver.dontUndo && (!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) && - UndoManager.AddEvent({ - redo: () => (receiver[prop] = value), - undo: () => { - const wasUpdate = receiver[UpdatingFromServer]; - const wasForce = receiver[ForceServerWrite]; - receiver[ForceServerWrite] = true; // needed since writes aren't propagated to server if UpdatingFromServerIsSet - receiver[UpdatingFromServer] = true; // needed if the event caused ACL's to change such that the doc is otherwise no longer editable. - receiver[prop] = curValue; - receiver[ForceServerWrite] = wasForce; - receiver[UpdatingFromServer] = wasUpdate; + UndoManager.AddEvent( + { + redo: () => (receiver[prop] = value), + undo: () => { + const wasUpdate = receiver[UpdatingFromServer]; + const wasForce = receiver[ForceServerWrite]; + receiver[ForceServerWrite] = true; // needed since writes aren't propagated to server if UpdatingFromServerIsSet + receiver[UpdatingFromServer] = true; // needed if the event caused ACL's to change such that the doc is otherwise no longer editable. + receiver[prop] = curValue; + receiver[ForceServerWrite] = wasForce; + receiver[UpdatingFromServer] = wasUpdate; + }, + prop: prop?.toString(), }, - prop: prop?.toString(), - }); + value + ); return true; } return false; @@ -390,6 +395,7 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any diff?.op === '$addToSet' ? { redo: () => { + console.log('redo $add: ' + prop, diff.items); // bcz: uncomment to log undo receiver[prop].push(...diff.items.map((item: any) => item.value ?? item)); lastValue = ObjectField.MakeCopy(receiver[prop]); }, @@ -406,11 +412,12 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any }); lastValue = ObjectField.MakeCopy(receiver[prop]); }), - prop: '', + prop: 'add ' + diff.items.length + ' items to list', } : diff?.op === '$remFromSet' ? { redo: action(() => { + console.log('redo $rem: ' + prop, diff.items); // bcz: uncomment to log undo diff.items.forEach((item: any) => { const ind = item instanceof SchemaHeaderField ? receiver[prop].findIndex((ele: any) => ele instanceof SchemaHeaderField && ele.heading === item.heading) : receiver[prop].indexOf(item.value ?? item); ind !== -1 && receiver[prop].splice(ind, 1); @@ -430,10 +437,11 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any }); lastValue = ObjectField.MakeCopy(receiver[prop]); }, - prop: '', + prop: 'remove ' + diff.items.length + ' items from list', } : { redo: () => { + console.log('redo list: ' + prop, receiver[prop]); // bcz: uncomment to log undo receiver[prop] = ObjectField.MakeCopy(newValue as List); lastValue = ObjectField.MakeCopy(receiver[prop]); }, @@ -442,7 +450,7 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any receiver[prop] = ObjectField.MakeCopy(prevValue as List); lastValue = ObjectField.MakeCopy(receiver[prop]); }, - prop: '', + prop: 'assign list', } ); } -- cgit v1.2.3-70-g09d2 From bf16eca7a84adfdf1c5970e7e4793568ee70325d Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 13 Jun 2023 22:14:29 -0400 Subject: fixed updating cached docs when opening a backlinks collection. added some FieldInfo types and added 'enumeration' field display in schema view. fixed bug in schema view column sizing. updated a bunch of standard field names to be more consistent. --- src/client/documents/Documents.ts | 471 +++++++++++---------- src/client/util/CurrentUserUtils.ts | 115 ++--- src/client/util/DocumentManager.ts | 2 +- src/client/util/DragManager.ts | 2 +- src/client/util/LinkManager.ts | 2 +- src/client/util/TrackMovements.ts | 3 +- src/client/views/DashboardView.tsx | 9 +- src/client/views/DocComponent.tsx | 2 - src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/DocumentDecorations.tsx | 16 +- src/client/views/FilterPanel.tsx | 12 +- src/client/views/GestureOverlay.tsx | 8 +- src/client/views/LightboxView.tsx | 12 +- src/client/views/MainView.tsx | 20 +- src/client/views/MarqueeAnnotator.tsx | 2 +- src/client/views/OverlayView.tsx | 4 +- src/client/views/Palette.tsx | 4 +- src/client/views/PropertiesButtons.tsx | 4 +- .../views/PropertiesDocBacklinksSelector.tsx | 4 +- src/client/views/PropertiesView.tsx | 44 +- src/client/views/SidebarAnnos.tsx | 12 +- src/client/views/StyleProvider.tsx | 23 +- src/client/views/TemplateMenu.tsx | 4 +- src/client/views/collections/CollectionMenu.tsx | 10 +- .../views/collections/CollectionNoteTakingView.tsx | 104 ++--- .../views/collections/CollectionPileView.tsx | 21 +- .../collections/CollectionStackedTimeline.tsx | 14 +- .../views/collections/CollectionStackingView.tsx | 12 +- src/client/views/collections/CollectionSubView.tsx | 29 +- .../views/collections/CollectionTimeView.tsx | 18 +- .../views/collections/CollectionTreeView.tsx | 12 +- src/client/views/collections/TabDocView.tsx | 8 +- src/client/views/collections/TreeView.tsx | 24 +- .../CollectionFreeFormLinkView.tsx | 16 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 14 +- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- .../collectionLinear/CollectionLinearView.tsx | 18 +- .../CollectionMulticolumnView.tsx | 4 +- .../CollectionMultirowView.tsx | 4 +- .../collectionSchema/CollectionSchemaView.scss | 2 +- .../collectionSchema/CollectionSchemaView.tsx | 56 +-- .../collectionSchema/SchemaColumnHeader.tsx | 3 +- .../collections/collectionSchema/SchemaRowBox.tsx | 10 +- .../collectionSchema/SchemaTableCell.tsx | 55 ++- src/client/views/linking/LinkMenuGroup.tsx | 2 +- src/client/views/linking/LinkPopup.tsx | 4 +- .../views/nodes/DataVizBox/components/TableBox.tsx | 2 +- src/client/views/nodes/DocumentLinksButton.tsx | 6 +- src/client/views/nodes/DocumentView.tsx | 43 +- src/client/views/nodes/KeyValuePair.tsx | 4 +- src/client/views/nodes/LabelBox.tsx | 4 +- src/client/views/nodes/LinkAnchorBox.tsx | 4 +- src/client/views/nodes/LinkDocPreview.tsx | 4 +- src/client/views/nodes/MapBox/MapBox.tsx | 8 +- src/client/views/nodes/MapBox/MapBox2.tsx | 8 +- src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 4 +- src/client/views/nodes/QueryBox.scss | 5 - src/client/views/nodes/QueryBox.tsx | 38 -- src/client/views/nodes/VideoBox.tsx | 4 +- src/client/views/nodes/WebBox.tsx | 8 +- src/client/views/nodes/button/FontIconBox.tsx | 2 +- .../views/nodes/formattedText/DashDocView.tsx | 4 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 22 +- src/client/views/nodes/trails/PresBox.tsx | 14 +- src/client/views/nodes/trails/PresElementBox.tsx | 10 +- src/client/views/pdf/PDFViewer.tsx | 10 +- src/fields/Doc.ts | 44 +- src/fields/documentSchemas.ts | 10 +- src/fields/util.ts | 8 +- src/mobile/AudioUpload.tsx | 10 +- src/mobile/MobileInterface.tsx | 4 +- 72 files changed, 740 insertions(+), 761 deletions(-) delete mode 100644 src/client/views/nodes/QueryBox.scss delete mode 100644 src/client/views/nodes/QueryBox.tsx (limited to 'src/client/views/collections/CollectionStackedTimeline.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index c1abd6e71..a91d5806c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -110,18 +110,28 @@ class DocInfo extends FInfo { } } class DimInfo extends FInfo { - fieldType? = 'DimUnit'; + fieldType? = 'enumeration'; values? = [DimUnit.Pixel, DimUnit.Ratio]; - readOnly = true; + readOnly = false; } class PEInfo extends FInfo { - fieldType? = 'pointerEvents'; + fieldType? = 'enumeration'; values? = ['all', 'none']; - readOnly = true; + readOnly = false; } class DAInfo extends FInfo { - fieldType? = 'dropActionType'; + fieldType? = 'enumeration'; values? = ['embed', 'copy', 'move', 'same', 'proto', 'none']; + readOnly = false; +} +class CTypeInfo extends FInfo { + fieldType? = 'enumeration'; + values? = Array.from(Object.keys(CollectionViewType)); + readOnly = false; +} +class DTypeInfo extends FInfo { + fieldType? = 'enumeration'; + values? = Array.from(Object.keys(DocumentType)); readOnly = true; } class DateInfo extends FInfo { @@ -134,272 +144,267 @@ type STRt = StrInfo | string; type DOCt = DocInfo | Doc; type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio; type PEVt = PEInfo | 'none' | 'all'; +type COLLt = CTypeInfo | CollectionViewType; type DROPt = DAInfo | dropActionType; type DATEt = DateInfo | number; +type DTYPEt = DTypeInfo | string; export class DocumentOptions { + // coordinate and dimensions depending on view 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)', false, [1, 0]); - isSystem?: BOOLt = new BoolInfo('is this a system created/owned doc', true); - type?: STRt = new StrInfo('type of document', true, Array.from(Object.keys(DocumentType))); - title?: string; - author_date?: DATEt = new DateInfo('date the document was created', true); - _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)?', true); - 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?: 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'); - _layout_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'); - _freeform_panX?: NUMt = new NumInfo('horizontal pan location of a freeform view'); - _freeform_panY?: NUMt = new NumInfo('vertical pan location of a freeform view'); + _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"); + lat?: NUMt = new NumInfo('latitude coordinate for map views'); + lng?: NUMt = new NumInfo('longitude coordinate for map views'); + _timecodeToShow?: NUMt = new NumInfo('the time that a document should be displayed (e.g., when an annotation shows up as a video plays)'); + _timecodeToHide?: NUMt = new NumInfo('the time that a document should be hidden'); _width?: NUMt = new NumInfo('displayed width of a document'); _height?: NUMt = new NumInfo('displayed height of document'); data_nativeWidth?: NUMt = new NumInfo('native width of data field contents (e.g., the pixel width of an image)'); data_nativeHeight?: NUMt = new NumInfo('native height of data field contents (e.g., the pixel height of an image)'); + linearBtnWidth?: NUMt = new NumInfo('unexpanded width of a linear menu button (button "width" changes when it expands)'); _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)'); _nativeDimModifiable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers'); _nativeHeightUnfrozen?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers'); - _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"); + + 'acl-Public'?: string; // public permissions + '_acl-Public'?: string; // public permissions + type?: DTYPEt = new DTypeInfo('type of document', true); + _type_collection?: COLLt = new CTypeInfo('how collection is rendered'); // sub type of a collection + title?: STRt = new StrInfo('title of document'); + caption?: RichTextField; + author?: string; // STRt = new StrInfo('creator of document'); // bcz: don't change this. Otherwise, the userDoc's field Infos will have a FieldInfo assigned to its author field which will render it unreadable + author_date?: DATEt = new DateInfo('date the document was created', true); + annotationOn?: DOCt = new DocInfo('document annotated by this document'); + color?: STRt = new StrInfo('foreground color data doc'); + hidden?: BOOLt = new BoolInfo('whether the document is not rendered by its collection'); + backgroundColor?: STRt = new StrInfo('background color for data doc'); + opacity?: NUMt = new NumInfo('document opacity'); + viewTransitionTime?: NUMt = new NumInfo('transition duration for view parameters'); + dontRegisterView?: BOOLt = new BoolInfo('are views of this document registered so that they can be found when following links, etc'); + dontUndo?: BOOLt = new BoolInfo('whether button clicks should be undoable (true for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)'); + _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'); + _lockedPosition?: BOOLt = new BoolInfo("lock the x,y coordinates of the document so that it can't be dragged"); + _lockedTransform?: BOOLt = new BoolInfo('lock the freeform_panx,freeform_pany and scale parameters of the document so that it be panned/zoomed'); + + layout?: string | Doc; // default layout string or template document + layout_keyValue?: STRt = new StrInfo('layout definition for showing keyValue view of document'); + layout_explainer?: STRt = new StrInfo('explanation displayed at top of a collection to describe its purpose'); + layout_headerButton?: DOCt = new DocInfo('the (button) Doc to display at the top of a collection.'); + layout_disableBrushing?: BOOLt = new BoolInfo('whether to suppress border highlighting'); + layout_unrendered?: BOOLt = new BoolInfo('denotes an annotation that is not rendered with a DocumentView (e.g, rtf/pdf text selections and links to scroll locations in web/pdf)'); + layout_hideOpenButton?: BOOLt = new BoolInfo('whether to hide the open full screen button when selected'); + layout_hideDocumentButtonBar?: BOOLt = new BoolInfo('whether to hide the document decorations lower button bar when selected'); + layout_hideLinkAnchors?: BOOLt = new BoolInfo('suppresses link anchor dots from being displayed'); + layout_hideAllLinks?: BOOLt = new BoolInfo('whether all individual blue anchor dots should be hidden'); + layout_hideResizeHandles?: BOOLt = new BoolInfo('whether to hide the resize handles when selected'); + layout_hideLinkButton?: BOOLt = new BoolInfo('whether the blue link counter button should be hidden'); + layout_hideDecorationTitle?: BOOLt = new BoolInfo('whether to suppress the document decortations title when selected'); + layout_borderRounding?: string; + layout_boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow + layout_maxAutoHeight?: NUMt = new NumInfo('maximum height for newly created (eg, from pasting) text documents'); + _layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents'); + _layout_curPage?: NUMt = new NumInfo('current page of a PDF or other? paginated document'); + _layout_currentTimecode?: NUMt = new NumInfo('the current timecode of a time-based document (e.g., current time of a video) value is in seconds'); + _layout_hideContextMenu?: BOOLt = new BoolInfo('whether the context menu can be shown'); _layout_fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)'); - _layoutFitContentsToBox?: 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 freeform_panx,freeform_pany and scale parameters of the document so that it be panned/zoomed - _followLinkToggle?: boolean; // whether document, when clicked, toggles display of its link target + _layout_fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content'); + _layout_fieldKey?: STRt = new StrInfo('the field key containing the current layout definition'); + _layout_enableAltContentUI?: BOOLt = new BoolInfo('whether to show alternate content button'); _layout_showTitle?: string; // field name to display in header (:hover is an optional suffix) - _layout_altContentUI?: boolean; // whether to show alternate content button - _isLightbox?: boolean; // whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target + _layout_showSidebar?: BOOLt = new BoolInfo('whether an annotationsidebar should be displayed for text docuemnts'); _layout_showCaption?: string; // which field to display in the caption area. leave empty to have no caption - _layoutScrollTop?: number; // scroll location for pdfs - _noAutoscroll?: boolean; // whether collections autoscroll when this item is dragged - _chromeHidden?: boolean; // whether the editing chrome for a document is hidden - _searchDoc?: boolean; // is this a search document (used to change UI for search results in schema view) - _forceActive?: boolean; // flag to handle pointer events when not selected (or otherwise active) - enableDragWhenActive?: boolean; // allow dragging even if document contentts are active (e.g., tree, groups) - _stayInCollection?: boolean; // whether the document should remain in its collection when someone tries to drag and drop it elsewhere - _raiseWhenDragged?: boolean; // whether a document is brought to front when dragged. - _hideContextMenu?: boolean; // whether the context menu can be shown - _type_collection?: string; // sub type of a collection - type_collection?: string; // sub type of a collection - _gridGap?: number; // gap between items in masonry view - freeform?: string; // placeholder just so that the key value pane has a divider to render for freeform view data - _freeform_scale?: number; // how much a freeform view has been scaled (zoomed) - _overflow?: string; // set overflow behavior - _xMargin?: number; // gap between left edge of document and start of masonry/stacking layouts - _yMargin?: number; // gap between top edge of dcoument and start of masonry/stacking layouts - _xPadding?: number; - _yPadding?: number; - _carousel_index?: number; // which item index the carousel viewer is showing - _layout_showSidebar?: boolean; //whether an annotationsidebar should be displayed for text docuemnts + + _chromeHidden?: BOOLt = new BoolInfo('whether the editing chrome for a document is hidden'); + _gridGap?: NUMt = new NumInfo('gap between items in masonry view'); + _xMargin?: NUMt = new NumInfo('gap between left edge of document and start of masonry/stacking layouts'); + _yMargin?: NUMt = new NumInfo('gap between top edge of dcoument and start of masonry/stacking layouts'); + _xPadding?: NUMt = new NumInfo('x padding'); + _yPadding?: NUMt = new NumInfo('y padding'); _singleLine?: boolean; // whether label box is restricted to one line of text _createDocOnCR?: boolean; // whether carriage returns and tabs create new text documents - _minFontSize?: number; // minimum font size for labelBoxes - _maxFontSize?: number; // maximum font size for labelBoxes - _columnWidth?: number; - _columnsHideIfEmpty?: boolean; // whether stacking view column headings should be hidden - _text_fontSize?: string; - _text_fontFamily?: string; - _text_fontWeight?: string; - _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views - _layout_curPage?: number; // current page of a PDF or other? paginated document - _layout_currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video) value is in seconds - _currentFrame?: number; // the current frame of a frame-based collection (e.g., progressive slide) - _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 + _columnWidth?: NUMt = new NumInfo('width of table column'); + _columnsHideIfEmpty?: BOOLt = new BoolInfo('whether stacking view column headings should be hidden'); _caption_xMargin?: NUMt = new NumInfo('x margin of caption inside of a carousel collection', true); _caption_yMargin?: NUMt = new NumInfo('y margin of caption inside of a carousel collection', true); icon_nativeWidth?: NUMt = new NumInfo('native width of icon view', true); icon_nativeHeight?: NUMt = new NumInfo('native height of icon view', true); - dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true); - openFactoryLocation?: string; // an OpenWhere value to place the factory created document - openFactoryAsDelegate?: boolean; // - lat?: number; - lng?: number; - infoWindowOpen?: boolean; - author?: string; - _layout_fieldKey?: string; - fieldValues?: List; // possible field values used by fieldInfos - fieldType?: string; // type of afield used by fieldInfos - layout_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) - 'acl-Public'?: string; // public permissions - '_acl-Public'?: string; // public permissions - version?: string; // version identifier for a document - label?: string; - hidden?: boolean; - _hidden?: boolean; - pointerEvents?: string; // pointer events that the documentview should have - mediaState?: string; // status of audio/video media document: "pendingRecording", "recording", "paused", "playing" - recording?: boolean; // whether WebCam is recording or not - autoPlayAnchors?: boolean; // whether to play audio/video when an anchor is clicked in a stackedTimeline. - dontPlayLinkOnSelect?: boolean; // whether an audio/video should start playing when a link is followed to it. - linkSource?: Doc; // the source document for a collection of backlinks + _text_fontSize?: string; + _text_fontFamily?: string; + _text_fontWeight?: string; + _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views + + infoWindowOpen?: BOOLt = new BoolInfo('whether info window corresponding to pin is open (on MapDocuments)'); + _carousel_index?: NUMt = new NumInfo('which item index the carousel viewer is showing'); + _label_minFontSize?: NUMt = new NumInfo('minimum font size for labelBoxes'); + _label_maxFontSize?: NUMt = new NumInfo('maximum font size for labelBoxes'); + stroke_width?: NUMt = new NumInfo('width of an ink stroke'); + icon_label?: STRt = new StrInfo('label to use for a fontIcon doc (otherwise, the title is used)'); + mediaState?: STRt = new StrInfo('status of audio/video media document: "pendingRecording", "recording", "paused", "playing"'); + recording?: BOOLt = new BoolInfo('whether WebCam is recording or not'); + autoPlayAnchors?: BOOLt = new BoolInfo('whether to play audio/video when an anchor is clicked in a stackedTimeline.'); + dontPlayLinkOnSelect?: BOOLt = new BoolInfo('whether an audio/video should start playing when a link is followed to it.'); updateContentsScript?: ScriptField; // reactive script invoked when viewing a document that can update contents of a collection (or do anything) toolTip?: string; // tooltip to display on hover toolType?: string; // type of pen tool - expertMode?: boolean; // something available only in expert (not novice) mode + expertMode?: BOOLt = new BoolInfo('something available only in expert (not novice) mode'); + + contentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents contextMenuFilters?: List; contextMenuScripts?: List; contextMenuLabels?: List; contextMenuIcons?: List; - defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen - waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire. 'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait - dontUndo?: boolean; // whether button clicks should be undoable ( true for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack) - layout?: string | Doc; // default layout string for a document - contentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents - childLimitHeight?: number; // whether to limit the height of collection children. 0 - means height can be no bigger than width + childFilters_boolean?: string; + childFilters?: List; + childLimitHeight?: NUMt = new NumInfo('whether to limit the height of collection children. 0 - means height can be no bigger than width'); childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox layout in tree view) childLayoutString?: string; // template string for collection to use to render its children - childDocumentsActive?: boolean; // whether child documents are active when parent is document active - childDontRegisterViews?: boolean; - childHideLinkButton?: boolean; // hide link buttons on all children + childDocumentsActive?: BOOLt = new BoolInfo('whether child documents are active when parent is document active'); + childDontRegisterViews?: BOOLt = new BoolInfo('whether child document views should be registered so that they can be found when following links, etc'); + childHideLinkButton?: BOOLt = new BoolInfo('hide link buttons on all children'); childContextMenuFilters?: List; childContextMenuScripts?: List; childContextMenuLabels?: List; childContextMenuIcons?: List; - followLinkZoom?: boolean; // whether to zoom to the target of a link - layout_hideLinkButton?: boolean; // whether the blue link counter button should be hidden - disableDocBrushing?: boolean; // whether to suppress border highlighting - layout_hideDecorationTitle?: boolean; - hideOpenButton?: boolean; - layout_hideResizeHandles?: boolean; - hideDocumentButtonBar?: boolean; - 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; 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) - caption?: RichTextField; - opacity?: number; - defaultBackgroundColor?: string; - _layout_autoMoveAnchors?: boolean; // whether link endpoint should move around the edges of a document to make shortest path to other link endpoint - layout_hideLinkAnchors?: boolean; // suppresses link anchor dots from being displayed - isFolder?: boolean; - lastFrame?: number; // the last frame of a frame-based collection (e.g., progressive slide) - activeFrame?: number; // the active frame of a document in a frame base collection - appearFrame?: number; // the frame in which the document appears - viewTransitionTime?: number; // transition duration for view parameters - presPanX?: number; // panX saved as a view spec - presPanY?: number; // panY saved as a view spec - presViewScale?: number; // viewScale saved as a view Spec - presTransition?: number; //the time taken for the transition TO a document - presDuration?: number; //the duration of the slide in presentation view - presZoomText?: boolean; // whether text anchors should shown in a larger box when following links to make them stand out - borderRounding?: string; - boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow + + lastFrame?: NUMt = new NumInfo('the last frame of a frame-based collection (e.g., progressive slide)'); + activeFrame?: NUMt = new NumInfo('the active frame of a document in a frame base collection'); + appearFrame?: NUMt = new NumInfo('the frame in which the document appears'); + _currentFrame?: NUMt = new NumInfo('the current frame of a frame-based collection (e.g., progressive slide)'); + + isSystem?: BOOLt = new BoolInfo('is this a system created/owned doc', true); + isBaseProto?: BOOLt = new BoolInfo('is doc a base level prototype for data documents as opposed to data documents which are prototypes for layout documents. base protos are not cloned during a deep'); + isTemplateForField?: string; // the field key for which the containing document is a rendering template + isTemplateDoc?: BOOLt = new BoolInfo('is the document a template for creating other documents'); + isGroup?: BOOLt = new BoolInfo('should collection use a grouping UI behavior'); + isFolder?: BOOLt = new BoolInfo('is document a tree view folder'); + _isTimelineLabel?: BOOLt = new BoolInfo('is document a timeline label'); + _isLightbox?: BOOLt = new BoolInfo('whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target'); + + presPanX?: NUMt = new NumInfo('panX saved as a view spec'); + presPanY?: NUMt = new NumInfo('panY saved as a view spec'); + presViewScale?: NUMt = new NumInfo('viewScale saved as a view Spec'); + presTransition?: NUMt = new NumInfo('the time taken for the transition TO a document'); + presDuration?: NUMt = new NumInfo('the duration of the slide in presentation view'); + presZoomText?: BOOLt = new BoolInfo('whether text anchors should shown in a larger box when following links to make them stand out'); + data?: any; - isBaseProto?: boolean; // this Doc is base level prototype for data documents as opposed to data documents which are prototypes for layout documents. base protos are not cloned during a deep - dontRegisterView?: boolean; - lookupField?: ScriptField; // script that returns the value of a field. This script is passed the rootDoc, layoutDoc, field, and container of the document. see PresBox. + data_useCors?: BOOLt = new BoolInfo('whether CORS protocol should be used for web page'); columnHeaders?: List; // headers for stacking views schemaHeaders?: List; // headers for schema view - dockingConfig?: string; - annotationOn?: Doc; - followLinkToggle?: boolean; - isGroup?: boolean; // whether a collection should use a grouping UI behavior - _removeDropProperties?: List; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document + dockingConfig?: STRt = new StrInfo('configuration of golden layout windows (applies only if doc is rendered as a CollectionDockingView)'); + icon?: string; // icon used by fonticonbox to render button noteType?: string; - // BACKGROUND GRID + + // freeform properties _freeform_backgroundGrid?: boolean; + _freeform_scale?: NUMt = new NumInfo('how much a freeform view has been scaled (zoomed)'); + _freeform_panX?: NUMt = new NumInfo('horizontal pan location of a freeform view'); + _freeform_panY?: NUMt = new NumInfo('vertical pan location of a freeform view'); + _freeform_noAutoPan?: BOOLt = new BoolInfo('disables autopanning when this item is dragged'); + _freeform_noZoom?: BOOLt = new BoolInfo('disables zooming'); //BUTTONS buttonText?: string; - iconShape?: string; // shapes of the fonticon border btnType?: string; btnList?: List; docColorBtn?: string; userColorBtn?: string; - canClick?: string; script?: ScriptField; - numBtnMax?: number; - numBtnMin?: number; + numBtnMax?: NUMt = new NumInfo('maximum value of a number button'); + numBtnMin?: NUMt = new NumInfo('minimum value of a number button'); switchToggle?: boolean; badgeValue?: ScriptField; //LINEAR VIEW - linearViewIsExpanded?: boolean; // is linear view expanded - linearViewExpandable?: boolean; // can linear view be expanded - linearViewToggleButton?: string; // button to open close linear view group - linearViewSubMenu?: boolean; - linearBtnWidth?: number; - flexGap?: number; // Linear view flex gap + linearView_IsExpanded?: BOOLt = new BoolInfo('is linear view expanded'); + linearView_Expandable?: BOOLt = new BoolInfo('can linear view be expanded'); + linearView_SubMenu?: BOOLt = new BoolInfo('is doc a sub menu of more linear views'); + flexGap?: NUMt = new NumInfo('Linear view flex gap'); flexDirection?: 'unset' | 'row' | 'column' | 'row-reverse' | 'column-reverse'; - layout_linkView?: Doc; // view template for a link document - layout_keyValue?: string; // view tempalte for key value docs link_description?: string; // added for links link_relationship?: string; // type of relatinoship a link represents - layout_linkDisplay?: boolean; // whether a link line should be dipslayed between the two link anchors - layout_linkDisplayArrow?: boolean; // whether to display link's directional arrowhead + link_displayLine?: BOOLt = new BoolInfo('whether a link line should be dipslayed between the two link anchors'); + link_displayArrow?: BOOLt = new BoolInfo("whether to display link's directional arrowhead"); link_anchor_1?: Doc; link_anchor_2?: Doc; - link_anchor_1_useLinkSmallAnchor?: boolean; // whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection) - link_anchor_2_useLinkSmallAnchor?: boolean; // whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection) - ignoreClick?: boolean; + link_autoMoveAnchors?: BOOLt = new BoolInfo('whether link endpoint should move around the edges of a document to make shortest path to other link endpoint'); + link_anchor_1_useSmallAnchor?: BOOLt = new BoolInfo('whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection)'); + link_anchor_2_useSmallAnchor?: BOOLt = new BoolInfo('whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection)'); + link_relationshipList?: List; // for storing different link relationships (when set by user in the link editor) + link_relationshipSizes?: List; //stores number of links contained in each relationship + link_colorList?: List; // colors of links corresponding to specific link relationships + followLinkZoom?: BOOLt = new BoolInfo('whether to zoom to the target of a link'); + followLinkToggle?: BOOLt = new BoolInfo('whether target of link should be toggled on and off when following a link to it'); + followLinkLocation?: STRt = new StrInfo('where to open link target when following link'); + followLinkAnimEffect?: STRt = new StrInfo('animation effect triggered on target of link'); + followLinkAnimDirection?: STRt = new StrInfo('direction modifier for animation effect'); + + ignoreClick?: BOOLt = new BoolInfo('whether clicks on document should be ignored'); onClick?: ScriptField; onDoubleClick?: ScriptField; onChildClick?: ScriptField; // script given to children of a collection to execute when they are clicked onChildDoubleClick?: ScriptField; // script given to children of a collection to execute when they are double clicked + defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen + waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire. 'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait onPointerDown?: ScriptField; onPointerUp?: ScriptField; + dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true); + openFactoryLocation?: string; // an OpenWhere value to place the factory created document + openFactoryAsDelegate?: BOOLt = new BoolInfo('create a delegate of the factory'); + _forceActive?: BOOLt = new BoolInfo('flag to handle pointer events when not selected (or otherwise active)'); + enableDragWhenActive?: BOOLt = new BoolInfo('allow dragging even if document contentts are active (e.g., tree, groups)'); + _stayInCollection?: BOOLt = new BoolInfo('whether the document should remain in its collection when someone tries to drag and drop it elsewhere'); + _raiseWhenDragged?: BOOLt = new BoolInfo('whether a document is brought to front when dragged.'); + allowOverlayDrop?: BOOLt = new BoolInfo('can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?', true); + 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 ??? '); + _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else"); + _removeDropProperties?: List; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document + cloneFieldFilter?: List; // fields not to copy when the document is clonedclipboard?: Doc; dropConverter?: ScriptField; // script to run when documents are dropped on this Document. dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script clickFactory?: Doc; // document to create when clicking on a button with a suitable onClick script onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop - cloneFieldFilter?: List; // fields not to copy when the document is clonedclipboard?: Doc; - filterBoolean?: string; - data_useCors?: boolean; - icon?: string; // icon used by fonticonbox to render button - target?: Doc; // available for use in scripts as the primary target document - sourcePanel?: Doc; // panel to display in 'targetContainer' as the result of a button onClick script - targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script - searchFileTypes?: List; // file types allowed in a search query - stroke_width?: number; - freezeChildren?: string; // whether children are now allowed to be added and or removed from a collection - treeViewHideTitle?: boolean; // whether to hide the top document title of a tree view - treeViewHideUnrendered?: boolean; // tells tree view not to display documents that have an 'layout_unrendered' tag unless they also have a treeViewFieldKey tag (presBox) - treeViewHideHeaderIfTemplate?: boolean; // whether to hide the header for a document in a tree view only if a childLayoutTemplate is provided (presBox) - 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. + target?: Doc; // available for use in scripts. used to provide a document parameter to the script (Note, this is a convenience entry since any field could be used for parameterizing a script) + + treeViewHideTitle?: BOOLt = new BoolInfo('whether to hide the top document title of a tree view'); + treeViewHideUnrendered?: BOOLt = new BoolInfo("tells tree view not to display documents that have an 'layout_unrendered' tag unless they also have a treeViewFieldKey tag (presBox)"); + treeViewHideHeaderIfTemplate?: BOOLt = new BoolInfo('whether to hide the header for a document in a tree view only if a childLayoutTemplate is provided (presBox)'); + treeViewHideHeader?: BOOLt = new BoolInfo('whether to hide the header for a document in a tree view'); + treeViewHideHeaderFields?: BOOLt = new BoolInfo('whether to hide the drop down options for tree view items.'); treeViewChildDoubleClick?: ScriptField; // - // Action Button - buttonMenu?: boolean; // whether a action button should be displayed - buttonMenuDoc?: Doc; - explainer?: string; - - treeViewOpenIsTransient?: boolean; // ignores the treeViewOpen Doc flag, allowing a treeViewItem's expand/collapse state to be independent of other views of the same document in the same or any other tree view - _treeViewOpen?: boolean; // whether this document is expanded in a tree view (note: need _ and regular versions since this can be specified for both proto and layout docs) - treeViewOpen?: boolean; // whether this document is expanded in a tree view + treeViewOpenIsTransient?: BOOLt = new BoolInfo("ignores the treeViewOpen Doc flag, allowing a treeViewItem's expand/collapse state to be independent of other views of the same document in the same or any other tree view"); + treeViewOpen?: BOOLt = new BoolInfo('whether this document is expanded in a tree view'); treeViewExpandedView?: string; // which field/thing is displayed when this item is opened in tree view - treeViewExpandedViewLock?: boolean; // whether the expanded view can be changed + treeViewExpandedViewLock?: BOOLt = new BoolInfo('whether the expanded view can be changed'); treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked - treeViewTruncateTitleWidth?: number; - treeViewHasOverlay?: boolean; // whether the treeview has an overlay for freeform annotations + treeViewTruncateTitleWidth?: NUMt = new NumInfo('maximum width of a treew view title before truncation'); + treeViewHasOverlay?: BOOLt = new BoolInfo('whether the treeview has an overlay for freeform annotations'); treeViewType?: string; // whether treeview is a Slide, file system, or (default) collection hierarchy - sidebarColor?: string; // background color of text sidebar - sidebarViewType?: string; // collection type of text sidebar - docMaxAutoHeight?: number; // maximum height for newly created (eg, from pasting) text documents + treeViewFreezeChildren?: STRt = new StrInfo('set (add, remove, add|remove) to disable adding, removing or both from collection'); + + sidebar_color?: string; // background color of text sidebar + sidebar_collectionType?: string; // collection type of text sidebar + text?: string; - textTransform?: string; // is linear view expanded - letterSpacing?: string; // is linear view expanded + textTransform?: string; + letterSpacing?: string; iconTemplate?: string; // name of icon template style - selectedIndex?: number; // which item in a linear view has been selected using the "thumb doc" ui + selectedIndex?: NUMt = new NumInfo("which item in a linear view has been selected using the 'thumb doc' ui"); + + fieldValues?: List; // possible values a field can have (used by FieldInfo's only) + fieldType?: string; // display type of a field, e.g. string, number, enumeration (used by FieldInfo's only) + clipboard?: Doc; - searchQuery?: string; // for quersyBox - useLinkSmallAnchor?: boolean; // whether links to this document should use a miniature linkAnchorBox - border?: string; //for searchbox hoverBackgroundColor?: string; // background color of a label when hovered - link_relationshipList?: List; // for storing different link relationships (when set by user in the link editor) - link_relationshipSizes?: List; //stores number of links contained in each relationship - linkColorList?: List; // colors of links corresponding to specific link relationships + userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)'); } export namespace Docs { export let newAccount: boolean = false; @@ -986,7 +991,7 @@ export namespace Docs { I.layout = InkingStroke.LayoutString('stroke'); I.layout_fitWidth = true; I.layout_hideDecorationTitle = true; // don't show title when selected - // I.hideOpenButton = true; // don't show open full screen button when selected + // I.layout_hideOpenButton = true; // don't show open full screen button when selected I.color = color; I.fillColor = fillColor; I.stroke = new InkField(points); @@ -1088,7 +1093,12 @@ export namespace Docs { } export function PileDocument(documents: Array, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _overflow: 'visible', enableDragWhenActive: true, _forceActive: true, _noAutoscroll: true, ...options, _type_collection: CollectionViewType.Pile }, id); + return InstanceFromProto( + Prototypes.get(DocumentType.COL), + new List(documents), + { enableDragWhenActive: true, _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile }, + id + ); } export function LinearDocument(documents: Array, options: DocumentOptions, id?: string) { @@ -1177,12 +1187,7 @@ export namespace Docs { } export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { - return InstanceFromProto( - Prototypes.get(DocumentType.COL), - new List(documents), - { freezeChildren: 'remove|add', ...options, type_collection: CollectionViewType.Docking, _type_collection: CollectionViewType.Docking, dockingConfig: config }, - id - ); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeViewFreezeChildren: 'remove|add', ...options, _type_collection: CollectionViewType.Docking, dockingConfig: config }, id); } export function DirectoryImportDocument(options: DocumentOptions = {}) { @@ -1227,18 +1232,18 @@ export namespace Docs { export namespace DocUtils { /** * @param docs - * @param docFilters - * @param docRangeFilters + * @param childFilters + * @param childFiltersByRanges * @param parentCollection - * Given a list of docs and docFilters, @returns the list of Docs that match those filters + * Given a list of docs and childFilters, @returns the list of Docs that match those filters */ - export function FilterDocs(childDocs: Doc[], docFilters: string[], docRangeFilters: string[], parentCollection?: Doc) { - if (!docFilters?.length && !docRangeFilters?.length) { + export function FilterDocs(childDocs: Doc[], childFilters: string[], childFiltersByRanges: string[], parentCollection?: Doc) { + if (!childFilters?.length && !childFiltersByRanges?.length) { return childDocs.filter(d => !d.cookies); // remove documents that need a cookie if there are no filters to provide one } const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields - docFilters.forEach(filter => { + childFilters.forEach(filter => { const fields = filter.split(':'); const key = fields[0]; const value = fields[1]; @@ -1249,7 +1254,7 @@ export namespace DocUtils { filterFacets[key][value] = modifiers; }); - const filteredDocs = docFilters.length + const filteredDocs = childFilters.length ? childDocs.filter(d => { if (d.z) return true; // if the document needs a cookie but no filter provides the cookie, then the document does not pass the filter @@ -1293,7 +1298,7 @@ export namespace DocUtils { return Field.toString(d[facetKey] as Field).includes(value); }); // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria - if (parentCollection?.filterBoolean === 'OR') { + if (parentCollection?.childFilters_boolean === 'OR') { if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true; } // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria @@ -1301,14 +1306,14 @@ export namespace DocUtils { if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false; } } - return (parentCollection?.currentFilter as Doc)?.filterBoolean === 'OR' ? false : true; + return (parentCollection?.currentFilter as Doc)?.childFilters_boolean === 'OR' ? false : true; }) : childDocs; const rangeFilteredDocs = filteredDocs.filter(d => { - for (let i = 0; i < docRangeFilters.length; i += 3) { - const key = docRangeFilters[i]; - const min = Number(docRangeFilters[i + 1]); - const max = Number(docRangeFilters[i + 2]); + for (let i = 0; i < childFiltersByRanges.length; i += 3) { + const key = childFiltersByRanges[i]; + const min = Number(childFiltersByRanges[i + 1]); + const max = Number(childFiltersByRanges[i + 2]); const val = typeof d[key] === 'string' ? (Number(StrCast(d[key])).toString() === StrCast(d[key]) ? Number(StrCast(d[key])) : undefined) : Cast(d[key], 'number', null); if (val === undefined) { //console.log("Should 'undefined' pass range filter or not?") @@ -1325,11 +1330,11 @@ export namespace DocUtils { broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1)); return DocUtils.ActiveRecordings.map(audio => { const sourceDoc = getSourceDoc(); - return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { layout_linkDisplay: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' }); + return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { link_displayLine: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' }); }); } - export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string; layout_linkDisplay?: boolean }, id?: string, showPopup?: number[]) { + export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string; link_displayLine?: boolean }, id?: string, showPopup?: number[]) { if (!linkSettings.link_relationship) linkSettings.link_relationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; const sv = DocumentManager.Instance.getDocumentView(source); if (target.doc === Doc.UserDoc()) return undefined; @@ -1370,15 +1375,15 @@ export namespace DocUtils { source, target, { - title: ComputedField.MakeFunction('generateLinkTitle(self)') as any, - link_anchor_1_useLinkSmallAnchor: source.useLinkSmallAnchor ? true : undefined, - link_anchor_2_useLinkSmallAnchor: target.useLinkSmallAnchor ? true : undefined, 'acl-Public': SharingPermissions.Augment, '_acl-Public': SharingPermissions.Augment, - layout_linkDisplay: linkSettings.layout_linkDisplay, + title: ComputedField.MakeFunction('generateLinkTitle(self)') as any, + link_anchor_1_useSmallAnchor: source.useSmallAnchor ? true : undefined, + link_anchor_2_useSmallAnchor: target.useSmallAnchor ? true : undefined, + link_displayLine: linkSettings.link_displayLine, link_relationship: linkSettings.link_relationship, link_description: linkSettings.link_description, - _layout_autoMoveAnchors: true, + link_autoMoveAnchors: true, _layout_showCaption: 'link_description', _layout_showTitle: 'link_relationship', }, @@ -1706,14 +1711,14 @@ export namespace DocUtils { if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) { const pushpin = Docs.Create.FontIconDocument({ title: 'pushpin', - label: '', + icon_label: '', annotationOn: Cast(doc.annotationOn, Doc, null), followLinkToggle: true, icon: 'map-pin', x: Cast(doc.x, 'number', null), y: Cast(doc.y, 'number', null), backgroundColor: '#ACCEF7', - hideAllLinks: true, + layout_hideAllLinks: true, _width: 15, _height: 15, _xPadding: 0, @@ -1804,7 +1809,7 @@ export namespace DocUtils { _xMargin: noMargins ? 0 : undefined, _yMargin: noMargins ? 0 : undefined, annotationOn, - docMaxAutoHeight: maxHeight, + layout_maxAutoHeight: maxHeight, backgroundColor, _width: width || 200, _height: 35, @@ -1812,7 +1817,7 @@ export namespace DocUtils { y: y, _layout_fitWidth: true, _layout_autoHeight: true, - _layout_altContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards), + _layout_enableAltContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards), title, }); const template = Doc.UserDoc().defaultTextLayout; @@ -1842,18 +1847,18 @@ export namespace DocUtils { } /** - * uploadFilesToDocs will take in an array of Files, and creates documents for the - * new files. - * + * uploadFilesToDocs will take in an array of Files, and creates documents for the + * new files. + * * @param files an array of files that will be uploaded * @param options options to use while uploading - * @returns + * @returns */ export async function uploadFilesToDocs(files: File[], options: DocumentOptions) { const generatedDocuments: Doc[] = []; - // These files do not have overwriteDocs, so we do not set the guid and let the client generate one. - const fileNoGuidPairs: Networking.FileGuidPair[] = files.map(file => ({file})); + // These files do not have overwriteDocs, so we do not set the guid and let the client generate one. + const fileNoGuidPairs: Networking.FileGuidPair[] = files.map(file => ({ file })); const upfiles = await Networking.UploadFilesToServer(fileNoGuidPairs); for (const { @@ -1867,8 +1872,8 @@ export namespace DocUtils { export function uploadFileToDoc(file: File, options: DocumentOptions, overwriteDoc: Doc) { const generatedDocuments: Doc[] = []; - // Since this file has an overwriteDoc, we can set the client tracking guid to the overwriteDoc's guid. - Networking.UploadFilesToServer([{file, guid: overwriteDoc[Id]}]).then(upfiles => { + // Since this file has an overwriteDoc, we can set the client tracking guid to the overwriteDoc's guid. + Networking.UploadFilesToServer([{ file, guid: overwriteDoc[Id] }]).then(upfiles => { const { source: { name, type }, result, diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 6f6ac2d4a..59041862f 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -23,7 +23,7 @@ import { MainView } from "../views/MainView"; import { ButtonType } from "../views/nodes/button/FontIconBox"; import { OpenWhere } from "../views/nodes/DocumentView"; import { OverlayView } from "../views/OverlayView"; -import { DragManager } from "./DragManager"; +import { DragManager, dropActionType } from "./DragManager"; import { MakeTemplate } from "./DropConverter"; import { FollowLinkScript } from "./LinkFollower"; import { LinkManager } from "./LinkManager"; @@ -97,7 +97,7 @@ export class CurrentUserUtils { const reqdOpts:DocumentOptions = { title: "Experimental Tools", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true, - _stayInCollection: true, _hideContextMenu: true, _forceActive: true, isSystem: true, + _stayInCollection: true, _layout_hideContextMenu: true, _forceActive: true, isSystem: true, _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, }; const reqdScripts = { dropConverter : "convertToButtons(dragData)" }; @@ -136,7 +136,7 @@ export class CurrentUserUtils { const reqdClickList = reqdTempOpts.map(opts => { const allOpts = {...reqdClickOpts, ...opts.opts}; const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(doc => doc.title === opts.opts.title): undefined; - return DocUtils.AssignOpts(clickDoc, allOpts) ?? MakeTemplate(Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script, {heading:Doc.name, checked:"boolean", containingTreeView:Doc.name}), allOpts), true, opts.opts.title); + return DocUtils.AssignOpts(clickDoc, allOpts) ?? MakeTemplate(Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script, {heading:Doc.name, checked:"boolean", containingTreeView:Doc.name}), allOpts), true, opts.opts.title?.toString()); }); const reqdOpts:DocumentOptions = {title: "click editor templates", _height:75, isSystem: true}; @@ -192,7 +192,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, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts + textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 24, 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 }); @@ -260,10 +260,11 @@ export class CurrentUserUtils { 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 + scripts?:{[key:string]: any}, creator:(opts:DocumentOptions)=> any // how to create the empty thing if it doesn't exist }[] = [ {key: "Note", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _layout_autoHeight: true }}, - {key: "Flashcard", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _layout_autoHeight: true, _layout_altContentUI: true}}, + {key: "Flashcard", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _layout_autoHeight: true, _layout_enableAltContentUI: true}}, {key: "Equation", creator: opts => Docs.Create.EquationDocument(opts), opts: { _width: 300, _height: 35, }}, {key: "Noteboard", creator: opts => Docs.Create.NoteTakingDocument([], opts), opts: { _width: 250, _height: 200, _layout_fitWidth: true}}, {key: "Simulation", creator: opts => Docs.Create.SimulationDocument(opts), opts: { _width: 300, _height: 300, }}, @@ -274,11 +275,11 @@ export class CurrentUserUtils { {key: "Map", creator: opts => Docs.Create.MapDocument([], opts), opts: { _width: 800, _height: 600, _layout_fitWidth: true, _layout_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, recording:true, isSystem: true, cloneFieldFilter: new List(["isSystem"]) }}, - {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, onClick: FollowLinkScript()}}, + {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10}, 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, treeViewHideUnrendered: true}}, - {key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, targetDropAction: "embed" as any, treeViewHideTitle: true, _layout_fitWidth:true, _chromeHidden: true, boxShadow: "0 0" }}, + {key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, targetDropAction: "embed" as dropActionType, treeViewHideTitle: true, _layout_fitWidth:true, _chromeHidden: 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, treeViewHasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, @@ -287,7 +288,7 @@ export class CurrentUserUtils { }, funcs: {title: 'self.text?.Text'}}, ]; - emptyThings.forEach(thing => DocUtils.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, undefined, thing.funcs)); + emptyThings.forEach(thing => DocUtils.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, thing.scripts, thing.funcs)); return [ { toolTip: "Tap or drag to create a note", title: "Note", icon: "sticky-note", dragFactory: doc.emptyNote as Doc, clickFactory: DocCast(doc.emptyNote)}, @@ -321,7 +322,7 @@ export class CurrentUserUtils { const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => { const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined; const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit, - _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, + _width: 35, _height: 35, _layout_hideContextMenu: true, _stayInCollection: true, btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, isSystem: true, _removeDropProperties: new List(["_stayInCollection"]), }; @@ -329,7 +330,7 @@ export class CurrentUserUtils { }); const reqdOpts:DocumentOptions = { - title: "Basic Item Creators", _layout_showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, isSystem: true, + title: "Basic Item Creators", _layout_showTitle: "title", _xMargin: 0, _stayInCollection: 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, childDropAction: 'embed' }; @@ -367,14 +368,14 @@ export class CurrentUserUtils { const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined; const reqdBtnOpts:DocumentOptions = { title, icon, target, btnType: ButtonType.MenuButton, isSystem: true, dontUndo: true, dontRegisterView: true, - _width: 60, _height: 60, _stayInCollection: true, _hideContextMenu: true, + _width: 60, _height: 60, _stayInCollection: true, _layout_hideContextMenu: true, _removeDropProperties: new List(["_stayInCollection"]), }; return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs); }); const reqdStackOpts:DocumentOptions ={ - title: "menuItemPanel", childDropAction: "same", backgroundColor: Colors.DARK_GRAY, boxShadow: "rgba(0,0,0,0)", dontRegisterView: true, ignoreClick: true, + title: "menuItemPanel", childDropAction: "same", backgroundColor: Colors.DARK_GRAY, layout_boxShadow: "rgba(0,0,0,0)", dontRegisterView: true, ignoreClick: true, _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 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)" }); @@ -413,14 +414,14 @@ export class CurrentUserUtils { static mobileButton = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MulticolumnDocument(docs, { ...opts, _nativeWidth: 900, _nativeHeight: 250, _width: 900, _height: 250, _yMargin: 15, - borderRounding: "5px", boxShadow: "0 0", isSystem: true + layout_borderRounding: "5px", layout_boxShadow: "0 0", isSystem: true }) as any as Doc // sets up the text container for the information contained within the mobile button static mobileTextContainer = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MultirowDocument(docs, { ...opts, _nativeWidth: 450, _nativeHeight: 250, _width: 450, _height: 250, _yMargin: 25, - backgroundColor: "rgba(0,0,0,0)", borderRounding: "0", boxShadow: "0 0", ignoreClick: true, isSystem: true + backgroundColor: "rgba(0,0,0,0)", layout_borderRounding: "0", layout_boxShadow: "0 0", ignoreClick: true, isSystem: true }) as any as Doc // Sets up the title of the button @@ -458,7 +459,7 @@ export class CurrentUserUtils { 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, childDropAction: "embed", - _lockedPosition: true, _type_collection: CollectionViewType.Schema, _searchDoc: true, }); + _lockedPosition: true, _type_collection: CollectionViewType.Schema }); } /// Initializes the panel of draggable tools that is opened from the left sidebar. @@ -467,8 +468,8 @@ export class CurrentUserUtils { 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 reqdToolOps:DocumentOptions = { - title: "My Tools", isSystem: true, ignoreClick: true, boxShadow: "0 0", - _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, + title: "My Tools", isSystem: true, ignoreClick: true, layout_boxShadow: "0 0", + _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _layout_hideContextMenu: true, _chromeHidden: true, }; return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]); } @@ -480,10 +481,10 @@ export class CurrentUserUtils { const toggleDarkTheme = `this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`; const newDashboard = `createNewDashboard()`; - const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, + const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _stayInCollection: true, _layout_hideContextMenu: true, title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", isSystem: true }; const reqdBtnScript = {onClick: newDashboard,} - const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript); + const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.layout_headerButton), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript); const contextMenuScripts = [/*newDashboard*/] as string[]; const contextMenuLabels = [/*"Create New Dashboard"*/] as string[]; @@ -493,15 +494,15 @@ export class CurrentUserUtils { const childContextMenuLabels = ["Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard", "Reset Dashboard"];// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters const childContextMenuIcons = ["chalkboard", "tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters const reqdOpts:DocumentOptions = { - title: "My Dashboards", childHideLinkButton: true, freezeChildren: "remove|add", treeViewHideTitle: true, boxShadow: "0 0", childDontRegisterViews: true, + title: "My Dashboards", childHideLinkButton: true, treeViewFreezeChildren: "remove|add", treeViewHideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true, treeViewTruncateTitleWidth: 350, ignoreClick: true, - buttonMenu: true, buttonMenuDoc: newDashboardButton, childDropAction: "embed", + layout_headerButton: newDashboardButton, childDropAction: "embed", _layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true, contextMenuLabels:new List(contextMenuLabels), contextMenuIcons:new List(contextMenuIcons), childContextMenuLabels:new List(childContextMenuLabels), childContextMenuIcons:new List(childContextMenuIcons), - explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files." + layout_explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files." }; myDashboards = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts); if (Cast(myDashboards.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) { @@ -523,19 +524,19 @@ export class CurrentUserUtils { const newFolder = `TreeView_addNewFolder()`; const newFolderOpts: DocumentOptions = { - _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _width: 30, _height: 30, + _forceActive: true, _stayInCollection: true, _layout_hideContextMenu: true, _width: 30, _height: 30, title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", isSystem: true }; const newFolderScript = { onClick: newFolder}; - const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.buttonMenuDoc), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript); + const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.layout_headerButton), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript); const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, - title: "My Documents", buttonMenu: true, buttonMenuDoc: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", isSystem: true, - isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, boxShadow: "0 0", childDontRegisterViews: true, + title: "My Documents", layout_headerButton: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", isSystem: true, + isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, layout_boxShadow: "0 0", childDontRegisterViews: true, treeViewTruncateTitleWidth: 350, ignoreClick: true, childDropAction: "embed", childContextMenuLabels: new List(["Create new folder"]), childContextMenuIcons: new List(["plus"]), - explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard." + layout_explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard." }; myFilesystem = DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [myFileOrphans]); const childContextMenuScripts = [newFolder]; @@ -548,21 +549,21 @@ export class CurrentUserUtils { /// 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, - title: "My Recently Closed", buttonMenu: true, childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "embed", isSystem: true, - treeViewTruncateTitleWidth: 350, ignoreClick: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", + title: "My Recently Closed", childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "embed", isSystem: true, + treeViewTruncateTitleWidth: 350, ignoreClick: true, layout_boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", contextMenuLabels: new List(["Empty recently closed"]), contextMenuIcons:new List(["trash"]), - explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list." + layout_explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list." }; const recentlyClosed = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts); const clearAll = (target:string) => `getProto(${target}).data = new List([])`; - const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, + const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _layout_hideContextMenu: true, title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton, buttonText: "Empty", icon: "trash", isSystem: true, toolTip: "Empty recently closed",}; - const clearDocsButton = DocUtils.AssignDocField(recentlyClosed, "clearDocsBtn", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")}); + DocUtils.AssignDocField(recentlyClosed, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")}); - if (recentlyClosed.buttonMenuDoc !== clearDocsButton) Doc.GetProto(recentlyClosed).buttonMenuDoc = clearDocsButton; + //if (recentlyClosed.layout_headerButton !== clearDocsButton) Doc.GetProto(recentlyClosed).layout_headerButton = clearDocsButton; if (!Cast(recentlyClosed.contextMenuScripts, listSpec(ScriptField),null)?.find((script) => script.script.originalScript === clearAll("self"))) { recentlyClosed.contextMenuScripts = new List([ScriptField.MakeScript(clearAll("self"))!]) @@ -574,7 +575,7 @@ export class CurrentUserUtils { static setupUserDocView(doc: Doc, field:string) { const reqdOpts:DocumentOptions = { _lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view", - boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, isSystem: true, + layout_boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, isSystem: true, treeViewHideTitle: true, treeViewTruncateTitleWidth: 350 }; if (!doc[field]) DocUtils.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" }); @@ -582,14 +583,14 @@ export class CurrentUserUtils { } static linearButtonList = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, { - ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, boxShadow: "0 0", _forceActive: true, + ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, layout_boxShadow: "0 0", _forceActive: true, dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), _lockedPosition: true, isSystem: true, flexDirection: "row" }) static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({ - btnType: ButtonType.ToolButton, _forceActive: true, _hideContextMenu: true, - _removeDropProperties: new List([ "_hideContextMenu", "stayInCollection"]), + btnType: ButtonType.ToolButton, _forceActive: true, _layout_hideContextMenu: true, + _removeDropProperties: new List([ "_layout_hideContextMenu", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, isSystem: true, ...opts, }) @@ -609,8 +610,8 @@ export class CurrentUserUtils { ]; const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, defaultDoubleClick: 'ignore', dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts)); const dockBtnsReqdOpts:DocumentOptions = { - title: "docked buttons", _height: 40, flexGap: 0, boxShadow: "standard", childDropAction: 'embed', - childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true + title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDropAction: 'embed', + childDontRegisterViews: true, linearView_IsExpanded: 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 }); reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); @@ -701,12 +702,12 @@ export class CurrentUserUtils { { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 20, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}}, { title: "Num", icon:"",toolTip: "Frame Number (click to toggle edit mode)",btnType: ButtonType.TextButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}}, { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 20, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}}, - { title: "Text", icon: "Text", toolTip: "Text functions", subMenu: CurrentUserUtils.textTools(), expertMode: false, toolType:DocumentType.RTF, funcs: { linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available - { title: "Ink", icon: "Ink", toolTip: "Ink functions", subMenu: CurrentUserUtils.inkTools(), expertMode: false, toolType:DocumentType.INK, funcs: { linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available - { title: "Doc", icon: "Doc", toolTip: "Freeform Doc tools", subMenu: CurrentUserUtils.freeTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available - { title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available - { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected - { title: "Schema", icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions", subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} } // Only when Schema is selected + { title: "Text", icon: "Text", toolTip: "Text functions", subMenu: CurrentUserUtils.textTools(), expertMode: false, toolType:DocumentType.RTF, funcs: { linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available + { title: "Ink", icon: "Ink", toolTip: "Ink functions", subMenu: CurrentUserUtils.inkTools(), expertMode: false, toolType:DocumentType.INK, funcs: { linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available + { title: "Doc", icon: "Doc", toolTip: "Freeform Doc tools", subMenu: CurrentUserUtils.freeTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)`, linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available + { title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available + { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected + { title: "Schema", icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} } // Only when Schema is selected ]; } @@ -719,7 +720,7 @@ export class CurrentUserUtils { _nativeWidth: params.width ?? 30, _width: params.width ?? 30, _height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth, toolType: params.toolType, expertMode: params.expertMode, - _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, + _stayInCollection: true, _layout_hideContextMenu: true, _lockedPosition: true, _removeDropProperties: new List([ "_stayInCollection"]), }; const reqdFuncs:{[key:string]:any} = { @@ -731,7 +732,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", dontUndo:true, flexGap: 0, childDropAction: 'embed', childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }; + const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", dontUndo:true, flexGap: 0, childDropAction: 'embed', childDontRegisterViews: true, linearView_IsExpanded: 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 => { const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title); @@ -740,7 +741,7 @@ export class CurrentUserUtils { } else { const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, dontUndo: true, childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: params.scripts?.onClick ? false : true, - linearViewSubMenu: true, linearViewExpandable: true, }; + linearView_SubMenu: true, linearView_Expandable: true, }; const items = params.subMenu?.map(sub => this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title)) ); @@ -793,8 +794,8 @@ export class CurrentUserUtils { "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment, childDropAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true, // NOTE: treeViewHideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar - _layout_showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, - 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'" + _layout_showTitle: "title", treeViewHideTitle: 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'" }; DocUtils.AssignDocField(doc, "mySharedDocs", opts => Docs.Create.TreeDocument([], opts, sharingDocumentId + "layout", sharingDocumentId), sharedDocOpts, undefined, sharedScripts); @@ -803,17 +804,17 @@ export class CurrentUserUtils { /// Import option on the left side button panel static setupImportSidebar(doc: Doc, field:string) { const reqdOpts:DocumentOptions = { - title: "My Imports", _forceActive: true, buttonMenu: true, ignoreClick: true, _layout_showTitle: "title", - _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0, - childDropAction: "copy", _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true, - dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go." + title: "My Imports", _forceActive: true, ignoreClick: true, _layout_showTitle: "title", + _stayInCollection: true, _layout_hideContextMenu: true, childLimitHeight: 0, + childDropAction: "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.StackingDocument([], opts), reqdOpts); const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer", - _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, + _width: 30, _height: 30, _stayInCollection: true, _layout_hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, buttonText: "Import", icon: "upload", isSystem: true }; - DocUtils.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" }); + DocUtils.AssignDocField(myImports, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" }); return myImports; } /// Updates the UserDoc to have all required fields, docs, etc. No changes should need to be @@ -850,7 +851,7 @@ export class CurrentUserUtils { doc.defaultAclPrivate ?? (doc.defaultAclPrivate = false); doc.savedFilters ?? (doc.savedFilters = new List()); doc.filterDocCount = 0; - doc.freezeChildren = "remove|add"; + doc.treeViewFreezeChildren = "remove|add"; this.setupLinkDocs(doc, linkDatabaseId); this.setupSharedDocs(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 7a88ca991..612f16ce9 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -189,7 +189,7 @@ export class DocumentManager { while ( containerDocContext.length && containerDocContext[0]?.embedContainer && - DocCast(containerDocContext[0].embedContainer)?.type_collection !== CollectionViewType.Docking && + DocCast(containerDocContext[0].embedContainer)?._type_collection !== CollectionViewType.Docking && (includeExistingViews || !DocumentManager.Instance.getDocumentView(containerDocContext[0])) ) { containerDocContext = [Cast(containerDocContext[0].embedContainer, Doc, null), ...containerDocContext]; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index fb4a8985c..668de5408 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -517,7 +517,7 @@ export namespace DragManager { const target = document.elementFromPoint(e.x, e.y); - if (target && !Doc.UserDoc()._noAutoscroll && !options?.noAutoscroll && !dragData.draggedDocuments?.some((d: any) => d._noAutoscroll)) { + if (target && !Doc.UserDoc()._noAutoscroll && !options?.noAutoscroll && !dragData.draggedDocuments?.some((d: any) => d._freeform_noAutoPan)) { const autoScrollHandler = () => { target.dispatchEvent( new CustomEvent('dashDragAutoScroll', { diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 41c2fa889..dbb05917e 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -134,7 +134,7 @@ export class LinkManager { public createlink_relationshipLists = () => { //create new lists for link relations and their associated colors if the lists don't already exist !Doc.UserDoc().link_relationshipList && (Doc.UserDoc().link_relationshipList = new List()); - !Doc.UserDoc().linkColorList && (Doc.UserDoc().linkColorList = new List()); + !Doc.UserDoc().link_ColorList && (Doc.UserDoc().link_ColorList = new List()); !Doc.UserDoc().link_relationshipSizes && (Doc.UserDoc().link_relationshipSizes = new List()); }; diff --git a/src/client/util/TrackMovements.ts b/src/client/util/TrackMovements.ts index f83b6af0e..0e56ee1bc 100644 --- a/src/client/util/TrackMovements.ts +++ b/src/client/util/TrackMovements.ts @@ -3,6 +3,7 @@ import { NumCast } from '../../fields/Types'; import { Doc, DocListCast } from '../../fields/Doc'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { Id } from '../../fields/FieldSymbols'; +import { CollectionViewType } from '../documents/DocumentTypes'; export type Movement = { time: number; @@ -89,7 +90,7 @@ export class TrackMovements { if (this.recordingFFViews === null) return; // so that the size comparisons are correct, we must filter to only the FFViews - const isFFView = (doc: Doc) => doc && 'type_collection' in doc && doc.type_collection === 'freeform'; + const isFFView = (doc: Doc) => doc && doc._type_collection === CollectionViewType.Freeform; const tabbedFFViews = new Set(); for (const DashDoc of tabbedDocs) { if (isFFView(DashDoc)) tabbedFFViews.add(DashDoc); diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 94dd010c3..50cf2226e 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -390,7 +390,7 @@ export class DashboardView extends React.Component { _width: 30, _height: 30, _stayInCollection: true, - _hideContextMenu: true, + _layout_hideContextMenu: true, title: 'New trail', toolTip: 'Create new trail', btnType: ButtonType.ClickButton, @@ -415,16 +415,15 @@ export class DashboardView extends React.Component { childDropAction: 'embed', treeViewTruncateTitleWidth: 150, ignoreClick: true, - buttonMenu: true, - buttonMenuDoc: myTrailsBtn, + layout_headerButton: myTrailsBtn, contextMenuIcons: new List(['plus']), contextMenuLabels: new List(['Create New Trail']), _lockedPosition: true, - boxShadow: '0 0', + layout_boxShadow: '0 0', childDontRegisterViews: true, targetDropAction: 'same', isSystem: true, - explainer: 'All of the trails that you have created will appear here.', + layout_explainer: 'All of the trails that you have created will appear here.', }; const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeViewChildDoubleClick: 'openPresentation(documentView.rootDoc)' }); dashboardDoc.myTrails = new PrefetchProxy(myTrails); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 6fab4430d..db24229dc 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -128,8 +128,6 @@ export function ViewBoxAnnotatableComponent

() isAnyChildContentActive = () => this._isAnyChildContentActive; - lookupField = (field: string) => ScriptCast((this.layoutDoc as any).lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field }).result; - protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; @computed public get annotationKey() { diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 547d844ca..35b0b22a8 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -589,7 +589,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV (link.layout_linkDisplay = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))} + linkCreated={link => (link.link_displayLine = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))} linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.(true)} linkFrom={() => this.props.views().lastElement()?.rootDoc} /> diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index e4d50b143..528b82e3e 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -332,7 +332,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2); const ratio = dist / maxDist; const radius = Math.min(1, ratio) * docMax; - doc.borderRounding = `${radius}px`; + doc.layout_borderRounding = `${radius}px`; }); return false; }, // moveEvent @@ -588,7 +588,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P let dW = docwidth * (dWin / refWidth); let dH = docheight * (dHin / refHeight); const scale = docView.props.ScreenToLocalTransform().Scale; - const modifyNativeDim = (e.ctrlKey || doc.layout_forceReflow) && doc.nativeDimModifiable && ((!dragBottom && !dragTop) || e.ctrlKey || doc.nativeHeightUnfrozen); + const modifyNativeDim = (e.ctrlKey && doc.nativeDimModifiable) || (doc.layout_forceReflow && !dragBottom && !dragTop) || (doc.nativeHeightUnfrozen && (dragBottom || dragTop || e.ctrlKey)); if (nwidth && nheight) { if (nwidth / nheight !== docwidth / docheight && !dragBottom && !dragTop) { docheight = (nheight / nwidth) * docwidth; @@ -636,7 +636,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } } if (!modifyNativeDim) { - actualdH = Math.min((nheight / nwidth) * docwidth, actualdH); + actualdH = (nheight / nwidth) * docwidth; //, actualdH); } doc._height = actualdH; } @@ -750,13 +750,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations; const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating; const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating; - const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.hideDocumentButtonBar || this._isRounding || this._isRotating; + const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.layout_hideDocumentButtonBar || this._isRounding || this._isRotating; // if multiple documents have been opened at the same time, then don't show open button const hideOpenButton = hideDecorations || seldocview.props.hideOpenButton || - seldocview.rootDoc.hideOpenButton || - SelectionManager.Views().some(docView => docView.props.Document._stayInCollection || docView.props.Document.isGroup || docView.props.Document.hideOpenButton) || + seldocview.rootDoc.layout_hideOpenButton || + SelectionManager.Views().some(docView => docView.rootDoc._stayInCollection || docView.rootDoc.isGroup || docView.rootDoc.layout_hideOpenButton) || this._isRounding || this._isRotating; const hideDeleteButton = @@ -767,7 +767,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P seldocview.rootDoc.hideDeleteButton || SelectionManager.Views().some(docView => { const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DocData]) : AclEdit; - return (docView.rootDoc.stayInCollection && !docView.rootDoc.timelineLabel) || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin); + return (docView.rootDoc.stayInCollection && !docView.rootDoc._isTimelineLabel) || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin); }); const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => ( @@ -796,7 +796,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P // Radius constants const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView; - const borderRadius = numberValue(StrCast(seldocview.rootDoc.borderRounding)); + const borderRadius = numberValue(StrCast(seldocview.rootDoc.layout_borderRounding)); const docMax = Math.min(NumCast(seldocview.rootDoc.width) / 2, NumCast(seldocview.rootDoc.height) / 2); const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2); const radiusHandle = (borderRadius / docMax) * maxDist; diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 53c1f1018..fe42628cd 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -64,7 +64,7 @@ export class FilterPanel extends React.Component { * The current attributes selected to filter based on */ @computed get activeFilters() { - return StrListCast(this.targetDoc?._docFilters); + return StrListCast(this.targetDoc?._childFilters); } /** @@ -108,8 +108,8 @@ export class FilterPanel extends React.Component { @observable _chosenFacets = new ObservableMap(); @computed get activeFacets() { const facets = new Map(this._chosenFacets); - StrListCast(this.targetDoc?._docFilters).map(filter => facets.set(filter.split(':')[0], filter.split(':')[2] === 'match' ? 'text' : 'checkbox')); - setTimeout(() => StrListCast(this.targetDoc?._docFilters).map(action(filter => this._chosenFacets.set(filter.split(':')[0], filter.split(':')[2] === 'match' ? 'text' : 'checkbox')))); + StrListCast(this.targetDoc?._childFilters).map(filter => facets.set(filter.split(':')[0], filter.split(':')[2] === 'match' ? 'text' : 'checkbox')); + setTimeout(() => StrListCast(this.targetDoc?._childFilters).map(action(filter => this._chosenFacets.set(filter.split(':')[0], filter.split(':')[2] === 'match' ? 'text' : 'checkbox')))); return facets; } /** @@ -174,7 +174,7 @@ export class FilterPanel extends React.Component { this.targetDoc && (this.targetDoc._filterBoolean = (e.target as any).value))} defaultValue={StrCast(this.targetDoc?.filterBoolean)}> + filter.split(':')[0] === facetHeader) ?.split(':')[1] ?? '-empty-' } @@ -218,7 +218,7 @@ export class FilterPanel extends React.Component { filter.split(':')[0] === facetHeader && filter.split(':')[1] == facetValue) ?.split(':')[2] === 'check' } diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 141e99c66..0951bff22 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -140,7 +140,7 @@ export class GestureOverlay extends Touchable { title: 'buttons', _layout_autoHeight: true, _yMargin: 5, - linearViewIsExpanded: true, + linearView_IsExpanded: true, backgroundColor: 'white', isSystem: true, }); @@ -148,7 +148,7 @@ export class GestureOverlay extends Touchable { _width: 300, _height: 25, _layout_autoHeight: true, - linearViewIsExpanded: true, + linearView_IsExpanded: true, flexDirection: 'column', isSystem: true, }); @@ -1013,8 +1013,8 @@ export class GestureOverlay extends Touchable { focus={emptyFunction} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} - docRangeFilters={returnEmptyFilter} - docFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} + childFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} /> ); diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 1b309a6be..286d39943 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -43,7 +43,7 @@ export class LightboxView extends React.Component { @observable private static _layoutTemplateString: Opt; @observable private static _doc: Opt; @observable private static _docTarget: Opt; - @observable private static _docFilters: string[] = []; // filters + @observable private static _childFilters: string[] = []; // filters private static _savedState: Opt; private static _history: Opt<{ doc: Doc; target?: Doc }[]> = []; @observable private static _future: Opt = []; @@ -58,7 +58,7 @@ export class LightboxView extends React.Component { this.LightboxDoc.layout_fieldKey = this._savedState.layout_fieldKey; } if (!doc) { - this._docFilters && (this._docFilters.length = 0); + this._childFilters && (this._childFilters.length = 0); this._future = this._history = []; Doc.ActiveTool = InkTool.None; MainView.Instance._exploreMode = false; @@ -134,7 +134,7 @@ export class LightboxView extends React.Component { @action public static SetCookie(cookie: string) { if (this.LightboxDoc && cookie) { - this._docFilters = (f => (this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f]))(`cookies:${cookie}:provide`); + this._childFilters = (f => (this._childFilters ? [this._childFilters.push(f) as any, this._childFilters][1] : [f]))(`cookies:${cookie}:provide`); } } public static AddDocTab = (doc: Doc, location: OpenWhere, layoutTemplate?: Doc | string) => { @@ -148,7 +148,7 @@ export class LightboxView extends React.Component { layoutTemplate ); }; - docFilters = () => LightboxView._docFilters || []; + childFilters = () => LightboxView._childFilters || []; addDocTab = LightboxView.AddDocTab; @action public static Next() { const doc = LightboxView._doc!; @@ -261,8 +261,8 @@ export class LightboxView extends React.Component { renderDepth={0} rootSelected={returnTrue} docViewPath={returnEmptyDoclist} - docFilters={this.docFilters} - docRangeFilters={returnEmptyFilter} + childFilters={this.childFilters} + childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} addDocument={undefined} removeDocument={undefined} diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 8e112dddb..b0e992cb6 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -608,8 +608,8 @@ export class MainView extends React.Component { focus={emptyFunction} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} />

@@ -637,8 +637,8 @@ export class MainView extends React.Component { focus={emptyFunction} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} suppressSetHeight={true} renderDepth={this._hideUI ? 0 : -1} @@ -736,8 +736,8 @@ export class MainView extends React.Component { focus={emptyFunction} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} />
@@ -767,8 +767,8 @@ export class MainView extends React.Component { isContentActive={returnTrue} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} scriptContext={this} /> @@ -903,8 +903,8 @@ export class MainView extends React.Component { renderDepth={0} focus={emptyFunction} whenChildContentsActiveChanged={emptyFunction} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} /> {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ?
{StrCast(this.userDoc?.presentationMode)}
: <>} diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 6c36d39b9..a4a2c1df9 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -126,7 +126,7 @@ export class MarqueeAnnotator extends React.Component { if (!e.aborted && e.linkDocument) { Doc.GetProto(e.linkDocument).link_relationship = 'cropped image'; Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.docView.rootDoc.title; - Doc.GetProto(e.linkDocument).layout_linkDisplay = false; + Doc.GetProto(e.linkDocument).link_displayLine = false; } }, }); diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 59865cba3..339507f65 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -228,8 +228,8 @@ export class OverlayView extends React.Component { docViewPath={returnEmptyDoclist} addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={emptyFunction} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} />
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx index 3ad28c418..749eb08a2 100644 --- a/src/client/views/Palette.tsx +++ b/src/client/views/Palette.tsx @@ -56,8 +56,8 @@ export default class Palette extends React.Component { styleProvider={returnEmptyString} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} />
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 76828a576..11b89fd69 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -133,14 +133,14 @@ export class PropertiesButtons extends React.Component<{}, {}> { const containerDoc = dv.rootDoc; //containerDoc.followAllLinks = // containerDoc.noShadow = - // containerDoc.disableDocBrushing = + // containerDoc.layout_disableBrushing = // containerDoc._forceActive = //containerDoc._freeform_fitContentsToBox = containerDoc._isLightbox = !containerDoc._isLightbox; //containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined; const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]); //dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' }); - containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.layout_linkDisplay = false))); + containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.link_displayLine = false))); }); } ); diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx index 7b21629da..3e69bcba6 100644 --- a/src/client/views/PropertiesDocBacklinksSelector.tsx +++ b/src/client/views/PropertiesDocBacklinksSelector.tsx @@ -19,8 +19,8 @@ type PropertiesDocBacklinksSelectorProps = { @observer export class PropertiesDocBacklinksSelector extends React.Component { getOnClick = (link: Doc) => { - const linkSource = this.props.Document; - const other = LinkManager.getOppositeAnchor(link, linkSource); + const linkAnchor = this.props.Document; + const other = LinkManager.getOppositeAnchor(link, linkAnchor); const otherdoc = !other ? undefined : other.annotationOn && other.type !== DocumentType.RTF ? Cast(other.annotationOn, Doc, null) : other; if (otherdoc) { diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 00e077f96..1f2e21dd5 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -276,8 +276,8 @@ export class PropertiesView extends React.Component { PanelHeight={panelHeight} focus={emptyFunction} ScreenToLocalTransform={this.getTransform} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} addDocument={returnFalse} moveDocument={undefined} @@ -941,26 +941,26 @@ export class PropertiesView extends React.Component { } /** - * Updates this.filterDoc's currentFilter and saves the docFilters on the currentFilter + * Updates this.filterDoc's currentFilter and saves the childFilters on the currentFilter */ updateFilterDoc = (doc: Doc) => { if (this.selectedDoc) { if (doc === this.selectedDoc.currentFilter) return; // causes problems if you try to reapply the same doc - const savedDocFilters = doc._docFiltersList; - const currentDocFilters = this.selectedDoc._docFilters; - this.selectedDoc._docFilters = new List(); - (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters; + const savedDocFilters = doc._childFiltersList; + const currentDocFilters = this.selectedDoc._childFilters; + this.selectedDoc._childFilters = new List(); + (this.selectedDoc.currentFilter as Doc)._childFiltersList = currentDocFilters; this.selectedDoc.currentFilter = doc; - doc._docFiltersList = new List(); - this.selectedDoc._docFilters = savedDocFilters; + doc._childFiltersList = new List(); + this.selectedDoc._childFilters = savedDocFilters; - const savedDocRangeFilters = doc._docRangeFiltersList; - const currentDocRangeFilters = this.selectedDoc._docRangeFilters; - this.selectedDoc._docRangeFilters = new List(); - (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters; + const savedDocRangeFilters = doc._childFiltersByRangesList; + const currentDocRangeFilters = this.selectedDoc._childFiltersByRanges; + this.selectedDoc._childFiltersByRanges = new List(); + (this.selectedDoc.currentFilter as Doc)._childFiltersByRangesList = currentDocRangeFilters; this.selectedDoc.currentFilter = doc; - doc._docRangeFiltersList = new List(); - this.selectedDoc._docRangeFilters = savedDocRangeFilters; + doc._childFiltersByRangesList = new List(); + this.selectedDoc._childFiltersByRanges = savedDocRangeFilters; } }; @@ -1117,7 +1117,7 @@ export class PropertiesView extends React.Component { Doc.GetProto(LinkManager.currentLink).link_relationship = value; const linkRelationshipList = StrListCast(Doc.UserDoc().link_relationshipList); const linkRelationshipSizes = NumListCast(Doc.UserDoc().link_relationshipSizes); - const linkColorList = StrListCast(Doc.UserDoc().linkColorList); + const linkColorList = StrListCast(Doc.UserDoc().link_ColorList); // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color if (!linkRelationshipList?.includes(value)) { @@ -1328,8 +1328,8 @@ export class PropertiesView extends React.Component {

Show link

@@ -277,16 +277,16 @@ export class CollectionViewBaseChrome extends React.Component { - this.target._docFilters = undefined; + this.target._childFilters = undefined; this.target._searchFilterDocs = undefined; }), initialize: (button: Doc) => { const activeDash = Doc.ActiveDashboard; if (activeDash) { - button['target-docFilters'] = (Doc.MySearcher._docFilters || activeDash._docFilters) instanceof ObjectField ? ObjectField.MakeCopy((Doc.MySearcher._docFilters || activeDash._docFilters) as any as ObjectField) : undefined; + button['target-childFilters'] = (Doc.MySearcher._childFilters || activeDash._childFilters) instanceof ObjectField ? ObjectField.MakeCopy((Doc.MySearcher._childFilters || activeDash._childFilters) as any as ObjectField) : undefined; button['target-searchFilterDocs'] = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs as any as ObjectField) : undefined; } }, diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index fc265e2b9..675f23970 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -201,13 +201,17 @@ export class CollectionNoteTakingView extends CollectionSubView() { }; styleProvider = (doc: Doc | undefined, props: Opt, property: string) => { - if (property === StyleProp.BoxShadow && doc && DragManager.docsBeingDragged.includes(doc)) { - return `#9c9396 ${StrCast(doc?.boxShadow, '10px 10px 0.9vw')}`; - } - if (property === StyleProp.Opacity && doc) { - if (this.props.childOpacity) { - return this.props.childOpacity(); - } + switch (property) { + case StyleProp.BoxShadow: + if (doc && DragManager.docsBeingDragged.includes(doc)) { + return `#9c9396 ${StrCast(doc?.layout_boxShadow, '10px 10px 0.9vw')}`; + } + break; + case StyleProp.Opacity: + if (doc && this.props.childOpacity) { + return this.props.childOpacity(); + } + break; } return this.props.styleProvider?.(doc, props, property); }; @@ -251,11 +255,11 @@ export class CollectionNoteTakingView extends CollectionSubView() { onDoubleClick={this.onChildDoubleClickHandler} ScreenToLocalTransform={noteTakingDocTransform} focus={this.focusDocument} - docFilters={this.childDocFilters} + childFilters={this.childDocFilters} hideDecorationTitle={this.props.childHideDecorationTitle?.()} hideResizeHandles={this.props.childHideResizeHandles?.()} hideTitle={this.props.childHideTitle?.()} - docRangeFilters={this.childDocRangeFilters} + childFiltersByRanges={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} addDocument={this.props.addDocument} moveDocument={this.props.moveDocument} @@ -603,42 +607,6 @@ export class CollectionNoteTakingView extends CollectionSubView() { return eles; } - @computed get buttonMenu() { - const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null); - if (menuDoc) { - const width = NumCast(menuDoc._width, 30); - const height = NumCast(menuDoc._height, 30); - return ( -
- 35} - PanelHeight={() => 35} - renderDepth={this.props.renderDepth} - focus={emptyFunction} - styleProvider={this.props.styleProvider} - docViewPath={returnEmptyDoclist} - whenChildContentsActiveChanged={emptyFunction} - bringToFront={emptyFunction} - docFilters={this.props.docFilters} - docRangeFilters={this.props.docRangeFilters} - searchFilterDocs={this.props.searchFilterDocs} - /> -
- ); - } - } - @computed get nativeWidth() { return Doc.NativeWidth(this.layoutDoc); } @@ -658,35 +626,25 @@ export class CollectionNoteTakingView extends CollectionSubView() { render() { TraceMobx(); - const buttonMenu = this.rootDoc.buttonMenu; - const noviceExplainer = StrCast(this.rootDoc.explainer); return ( - <> - {buttonMenu || noviceExplainer ? ( -
- {buttonMenu ? this.buttonMenu : null} - {Doc.UserDoc().noviceMode && noviceExplainer ?
{noviceExplainer}
: null} -
- ) : null} -
(this._scroll = e.currentTarget.scrollTop))} - onPointerLeave={action(e => (this.docsDraggedRowCol.length = 0))} - onPointerMove={e => e.buttons && this.onPointerMove(false, e.clientX, e.clientY)} - onDragOver={e => this.onPointerMove(true, e.clientX, e.clientY)} - onDrop={this.onExternalDrop.bind(this)} - onContextMenu={this.onContextMenu} - onWheel={e => this.props.isContentActive(true) && e.stopPropagation()}> - {this.renderedSections} -
- +
(this._scroll = e.currentTarget.scrollTop))} + onPointerLeave={action(e => (this.docsDraggedRowCol.length = 0))} + onPointerMove={e => e.buttons && this.onPointerMove(false, e.clientX, e.clientY)} + onDragOver={e => this.onPointerMove(true, e.clientX, e.clientY)} + onDrop={this.onExternalDrop.bind(this)} + onContextMenu={this.onContextMenu} + onWheel={e => this.props.isContentActive(true) && e.stopPropagation()}> + {this.renderedSections} +
); } } diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index bbc91aa6e..57d9bbb49 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -23,7 +23,7 @@ export class CollectionPileView extends CollectionSubView() { componentDidMount() { if (this.layoutEngine() !== computePassLayout.name && this.layoutEngine() !== computeStarburstLayout.name) { - this.Document._pileLayoutEngine = computePassLayout.name; + this.Document._freeform_pileEngine = computePassLayout.name; } this._originalChrome = this.layoutDoc._chromeHidden; this.layoutDoc._chromeHidden = true; @@ -33,7 +33,7 @@ export class CollectionPileView extends CollectionSubView() { Object.values(this._disposers).forEach(disposer => disposer?.()); } - layoutEngine = () => StrCast(this.Document._pileLayoutEngine); + layoutEngine = () => StrCast(this.Document._freeform_pileEngine); @undoBatch addPileDoc = (doc: Doc | Doc[]) => { @@ -73,28 +73,29 @@ export class CollectionPileView extends CollectionSubView() { // toggles the pileup between starburst to compact toggleStarburst = action(() => { + this.layoutDoc._freeform_scale = undefined; if (this.layoutEngine() === computeStarburstLayout.name) { if (this.rootDoc[Width]() !== NumCast(this.rootDoc._starburstDiameter, 500)) { this.rootDoc._starburstDiameter = this.rootDoc[Width](); } const defaultSize = 110; - this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[Width]() / 2 - NumCast(this.layoutDoc._starburstPileWidth, defaultSize) / 2; - this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[Height]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2; - this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize); - this.layoutDoc._height = NumCast(this.layoutDoc._starburstPileHeight, defaultSize); + this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[Width]() / 2 - NumCast(this.layoutDoc._freeform_pileWidth, defaultSize) / 2; + this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[Height]() / 2 - NumCast(this.layoutDoc._freeform_pileHeight, defaultSize) / 2; + this.layoutDoc._width = NumCast(this.layoutDoc._freeform_pileWidth, defaultSize); + this.layoutDoc._height = NumCast(this.layoutDoc._freeform_pileHeight, defaultSize); DocUtils.pileup(this.childDocs, undefined, undefined, NumCast(this.layoutDoc._width) / 2, false); this.layoutDoc._freeform_panX = 0; this.layoutDoc._freeform_panY = -10; - this.props.Document._pileLayoutEngine = computePassLayout.name; + this.props.Document._freeform_pileEngine = computePassLayout.name; } else { const defaultSize = NumCast(this.rootDoc._starburstDiameter, 500); this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[Width]() / 2 - defaultSize / 2; this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[Height]() / 2 - defaultSize / 2; - this.layoutDoc._starburstPileWidth = this.layoutDoc[Width](); - this.layoutDoc._starburstPileHeight = this.layoutDoc[Height](); + this.layoutDoc._freeform_pileWidth = this.layoutDoc[Width](); + this.layoutDoc._freeform_pileHeight = this.layoutDoc[Height](); this.layoutDoc._freeform_panX = this.layoutDoc._freeform_panY = 0; this.layoutDoc._width = this.layoutDoc._height = defaultSize; - this.props.Document._pileLayoutEngine = computeStarburstLayout.name; + this.props.Document._freeform_pileEngine = computeStarburstLayout.name; } }); diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index b131d38d8..9d5cb257a 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -404,15 +404,15 @@ export class CollectionStackedTimeline extends CollectionSubView if (timelineOnly) { if (!left && time !== undefined && time <= NumCast(anchor[this.props.startTag])) time = undefined; Doc.SetInPlace(anchor, left ? this.props.startTag : this.props.endTag, time, true); - if (!left) Doc.SetInPlace(anchor, 'borderRounding', time !== undefined ? undefined : '100%', true); + if (!left) Doc.SetInPlace(anchor, 'layout_borderRounding', time !== undefined ? undefined : '100%', true); } else { anchor[left ? '_timecodeToShow' : '_timecodeToHide'] = time; } @@ -820,8 +820,8 @@ class StackedTimelineAnchor extends React.Component focus={focusFunc} isContentActive={returnFalse} searchFilterDocs={returnEmptyDoclist} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} rootSelected={returnFalse} onClick={script} onDoubleClick={this.props.layoutDoc.autoPlayAnchors ? undefined : doublescript} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 56f53afe6..3c0d8cbc3 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -346,11 +346,11 @@ export class CollectionStackingView extends CollectionSubView 35; @computed get buttonMenu() { - const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null); + const menuDoc: Doc = Cast(this.rootDoc.layout_headerButton, Doc, null); // TODO:glr Allow support for multiple buttons if (menuDoc) { const width: number = NumCast(menuDoc._width, 30); @@ -679,8 +679,8 @@ export class CollectionStackingView extends CollectionSubView
@@ -711,7 +711,7 @@ export class CollectionStackingView extends CollectionSubView {buttonMenu || noviceExplainer ? ( diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 967d1d40e..d1b7f6ff6 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -28,8 +28,8 @@ export function CollectionSubView(moreProps?: X) { private gestureDisposer?: GestureUtils.GestureEventDisposer; protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; protected _mainCont?: HTMLDivElement; - @observable _focusFilters: Opt; // docFilters that are overridden when previewing a link to an anchor which has docFilters set on it - @observable _focusRangeFilters: Opt; // docRangeFilters that are overridden when previewing a link to an anchor which has docRangeFilters set on it + @observable _focusFilters: Opt; // childFilters that are overridden when previewing a link to an anchor which has childFilters set on it + @observable _focusRangeFilters: Opt; // childFiltersByRanges that are overridden when previewing a link to an anchor which has childFiltersByRanges set on it protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view this.dropDisposer?.(); @@ -81,13 +81,13 @@ export function CollectionSubView(moreProps?: X) { get childDocList() { return Cast(this.dataField, listSpec(Doc)); } - collectionFilters = () => this._focusFilters ?? StrListCast(this.props.Document._docFilters); - collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._docRangeFilters, listSpec('string'), []); + collectionFilters = () => this._focusFilters ?? StrListCast(this.props.Document._childFilters); + collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._childFiltersByRanges, listSpec('string'), []); // child filters apply to the descendants of the documents in this collection - childDocFilters = () => [...(this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()]; + childDocFilters = () => [...(this.props.childFilters?.().filter(f => Utils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()]; // unrecursive filters apply to the documents in the collection, but no their children. See Utils.noRecursionHack - unrecursiveDocFilters = () => [...(this.props.docFilters?.().filter(f => !Utils.IsRecursiveFilter(f)) || [])]; - childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()]; + unrecursiveDocFilters = () => [...(this.props.childFilters?.().filter(f => !Utils.IsRecursiveFilter(f)) || [])]; + childDocRangeFilters = () => [...(this.props.childFiltersByRanges?.() || []), ...this.collectionRangeDocFilters()]; searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs); @computed.struct get childDocs() { TraceMobx(); @@ -109,20 +109,20 @@ export function CollectionSubView(moreProps?: X) { const childDocs = rawdocs.filter(d => !(d instanceof Promise) && GetEffectiveAcl(Doc.GetProto(d)) !== AclPrivate && (this.props.ignoreUnrendered || !d.layout_unrendered)).map(d => d as Doc); const childDocFilters = this.childDocFilters(); - const docRangeFilters = this.childDocRangeFilters(); + const childFiltersByRanges = this.childDocRangeFilters(); const searchDocs = this.searchFilterDocs(); - if (this.props.Document.dontRegisterView || (!childDocFilters.length && !this.unrecursiveDocFilters().length && !docRangeFilters.length && !searchDocs.length)) { + if (this.props.Document.dontRegisterView || (!childDocFilters.length && !this.unrecursiveDocFilters().length && !childFiltersByRanges.length && !searchDocs.length)) { return childDocs.filter(cd => !cd.cookies); // remove any documents that require a cookie if there are no filters to provide one } const docsforFilter: Doc[] = []; childDocs.forEach(d => { // dragging facets - const dragged = this.props.docFilters?.().some(f => f.includes(Utils.noDragsDocFilter)); + const dragged = this.props.childFilters?.().some(f => f.includes(Utils.noDragsDocFilter)); if (dragged && DragManager.docsBeingDragged.includes(d)) return false; - let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), docRangeFilters, this.props.Document).length > 0; + let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), childFiltersByRanges, this.props.Document).length > 0; if (notFiltered) { - notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, this.props.Document).length > 0; + notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, childFiltersByRanges, this.props.Document).length > 0; const fieldKey = Doc.LayoutFieldKey(d); const annos = !Field.toString(Doc.LayoutField(d) as Field).includes(CollectionView.name); const data = d[annos ? fieldKey + '_annotations' : fieldKey]; @@ -131,13 +131,14 @@ export function CollectionSubView(moreProps?: X) { let subDocs = [...DocListCast(data), ...DocListCast(side)]; if (subDocs.length > 0) { let newarray: Doc[] = []; - notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, childDocFilters, docRangeFilters, d).length); + notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, childDocFilters, childFiltersByRanges, d).length); while (subDocs.length > 0 && !notFiltered) { newarray = []; subDocs.forEach(t => { const fieldKey = Doc.LayoutFieldKey(t); const annos = !Field.toString(Doc.LayoutField(t) as Field).includes(CollectionView.name); - notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], childDocFilters, docRangeFilters, d).length)); + notFiltered = + notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !childFiltersByRanges.length) || DocUtils.FilterDocs([t], childDocFilters, childFiltersByRanges, d).length)); DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc)); annos && DocListCast(t[fieldKey + '_sidebar']).forEach(newdoc => newarray.push(newdoc)); }); diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 49a90d828..60e6815e5 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -125,8 +125,8 @@ export class CollectionTimeView extends CollectionSubView() { goTo = (prevFilterIndex: number) => { this.layoutDoc._pivotField = this.layoutDoc['_prevPivotFields' + prevFilterIndex]; - this.layoutDoc._docFilters = ObjectField.MakeCopy(this.layoutDoc['_prevDocFilter' + prevFilterIndex] as ObjectField); - this.layoutDoc._docRangeFilters = ObjectField.MakeCopy(this.layoutDoc['_prevDocRangeFilters' + prevFilterIndex] as ObjectField); + this.layoutDoc._childFilters = ObjectField.MakeCopy(this.layoutDoc['_prevDocFilter' + prevFilterIndex] as ObjectField); + this.layoutDoc._childFiltersByRanges = ObjectField.MakeCopy(this.layoutDoc['_prevDocRangeFilters' + prevFilterIndex] as ObjectField); this.layoutDoc._prevFilterIndex = prevFilterIndex; }; @@ -136,7 +136,7 @@ export class CollectionTimeView extends CollectionSubView() { if (prevFilterIndex > 0) { this.goTo(prevFilterIndex - 1); } else { - this.layoutDoc._docFilters = new List([]); + this.layoutDoc._childFilters = new List([]); } }; @@ -145,7 +145,7 @@ export class CollectionTimeView extends CollectionSubView() {
{ const filterVals = bounds.payload as string[]; @@ -292,7 +292,7 @@ ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBou pivotDoc._pivotField = filterVals[0]; } } - const newFilters = StrListCast(pivotDoc._docFilters); + const newFilters = StrListCast(pivotDoc._childFilters); if (newFilters.length && originalFilter.length && newFilters.lastElement() === originalFilter.lastElement()) { pivotDoc._prevFilterIndex = --prevFilterIndex; pivotDoc['_prevDocFilter' + prevFilterIndex] = undefined; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 969658238..ed1e0c067 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -240,8 +240,8 @@ export class CollectionTreeView extends CollectionSubView {StrCast(this.rootDoc.explainer)}
; + return !Doc.noviceMode || !this.rootDoc.layout_explainer ? null :
{StrCast(this.rootDoc.layout_explainer)}
; } return35 = () => 35; @computed get buttonMenu() { - const menuDoc = Cast(this.rootDoc.buttonMenuDoc, Doc, null); + const menuDoc = Cast(this.rootDoc.layout_headerButton, Doc, null); // To create a multibutton menu add a CollectionLinearView return !menuDoc ? null : (
@@ -343,8 +343,8 @@ export class CollectionTreeView extends CollectionSubView
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 710e33b1c..75e4e8abf 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -432,8 +432,8 @@ export class TabDocView extends React.Component { PanelWidth={this.PanelWidth} PanelHeight={this.PanelHeight} styleProvider={DefaultStyleProvider} - docFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist} - docRangeFilters={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist} + childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist} + childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist} searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist} addDocument={undefined} removeDocument={this.remDocTab} @@ -602,8 +602,8 @@ export class TabMinimapView extends React.Component { styleProvider={TabMinimapView.miniStyleProvider} addDocTab={this.props.addDocTab} pinToPres={TabDocView.PinDoc} - docFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist} - docRangeFilters={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist} + childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist} + childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist} searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist} fitContentsToBox={returnTrue} /> diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index ca9471f6f..9039875c1 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -119,7 +119,7 @@ export class TreeView extends React.Component { return 'TreeView(' + this.props.document.title + ')'; } // this makes mobx trace() statements more descriptive get defaultExpandedView() { - return this.doc.type_collection === CollectionViewType.Docking + return this.doc._type_collection === CollectionViewType.Docking ? this.fieldKey : this.props.treeView.dashboardMode ? this.fieldKey @@ -388,7 +388,7 @@ export class TreeView extends React.Component { }; const addDoc = inside ? localAdd : parentAddDoc; const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same') && moveDocument; - const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.freezeChildren).includes('add')) || forceAdd; + const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.treeViewFreezeChildren).includes('add')) || forceAdd; if (canAdd) { this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = true); const res = UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false)); @@ -714,7 +714,7 @@ export class TreeView extends React.Component { const data = () => (this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : ''); const embeddings = () => (this.props.treeView.dashboardMode ? '' : 'embeddings'); const fields = () => (Doc.noviceMode ? '' : 'fields'); - const layout = Doc.noviceMode || this.doc.type_collection === CollectionViewType.Docking ? [] : ['layout']; + const layout = Doc.noviceMode || this.doc._type_collection === CollectionViewType.Docking ? [] : ['layout']; return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [embeddings(), links(), annos()] : []), fields()].filter(m => m); } @action @@ -732,7 +732,7 @@ export class TreeView extends React.Component { return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? null : ( <> {customHeaderButtons} {/* e.g.,. hide button is set by dashboardStyleProvider */} - {this.doc.hideContextMenu ? null : ( + {this.doc._layout_hideContextMenu ? null : ( { ? [] : this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ? [openEmbedding, makeFolder] - : this.doc.type_collection === CollectionViewType.Docking + : this.doc._type_collection === CollectionViewType.Docking ? [] : [deleteItem, openEmbedding, focusDoc]), ]; @@ -930,13 +930,13 @@ export class TreeView extends React.Component { focus={this.refocus} whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} bringToFront={emptyFunction} - disableDocBrushing={this.props.treeView.props.disableDocBrushing} + disableBrushing={this.props.treeView.props.disableBrushing} hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)} dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)} xPadding={NumCast(this.props.treeView.props.Document.childXPadding, this.props.treeView.props.childXPadding)} yPadding={NumCast(this.props.treeView.props.Document.childYPadding, this.props.treeView.props.childYPadding)} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} /> ); @@ -1010,8 +1010,8 @@ export class TreeView extends React.Component { treeViewDoc={this.props.treeView?.props.Document} rootSelected={returnTrue} docViewPath={this.props.treeView.props.docViewPath} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} addDocument={this.props.addDocument} moveDocument={this.move} @@ -1021,7 +1021,7 @@ export class TreeView extends React.Component { yPadding={NumCast(this.props.treeView.props.Document.childYPadding, this.props.treeView.props.childYPadding)} addDocTab={this.props.addDocTab} pinToPres={this.props.treeView.props.pinToPres} - disableDocBrushing={this.props.treeView.props.disableDocBrushing} + disableBrushing={this.props.treeView.props.disableBrushing} bringToFront={returnFalse} scriptContext={this} /> @@ -1211,7 +1211,7 @@ export class TreeView extends React.Component { onCheckedClick={onCheckedClick} onChildClick={onChildClick} renderDepth={renderDepth} - removeDoc={StrCast(treeViewParent.freezeChildren).includes('remove') ? undefined : remove} + removeDoc={StrCast(treeViewParent.treeViewFreezeChildren).includes('remove') ? undefined : remove} addDocument={addDocument} styleProvider={styleProvider} panelWidth={rowWidth} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 6202dcacc..f1d98d22a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -59,7 +59,7 @@ export class CollectionFreeFormLinkView extends React.Component (!LinkDocs.length || !linkDoc.layout_linkDisplay) && (this._opacity = 0.05)), + action(() => (!LinkDocs.length || !linkDoc.link_displayLine) && (this._opacity = 0.05)), 750 ); // this will unhighlight the link line. const a = A.ContentDiv.getBoundingClientRect(); @@ -77,7 +77,7 @@ export class CollectionFreeFormLinkView extends React.Component; - const linkColorList = Doc.UserDoc().linkColorList as List; + const linkColorList = Doc.UserDoc().link_ColorList as List; const linkRelationshipSizes = Doc.UserDoc().link_relationshipSizes as List; const currRelationshipIndex = linkRelationshipList.indexOf(linkRelationship); const linkDescription = Field.toString(link.link_description as any as Field); @@ -262,11 +262,11 @@ export class CollectionFreeFormLinkView extends React.Component @@ -295,7 +295,7 @@ export class CollectionFreeFormLinkView extends React.Component {textX === undefined || !linkDescription ? null : ( diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 811a77fa8..11151e74e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1021,7 +1021,7 @@ export class CollectionFreeFormView extends CollectionSubView { - if (this.Document._isGroup) return; + if (this.Document._isGroup || this.Document._freeform_noZoom) return; let deltaScale = deltaY > 0 ? 1 / 1.05 : 1.05; if (deltaScale < 0) deltaScale = -deltaScale; const [x, y] = this.getTransform().transformPoint(pointX, pointY); @@ -1294,8 +1294,8 @@ export class CollectionFreeFormView extends CollectionSubView ); } @@ -1692,7 +1692,7 @@ export class CollectionFreeFormView extends CollectionSubView 5 + NumCast(this.rootDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (doc[Width]() || this.dimension()) + tot + 4, 0) : 0), + () => 5 + NumCast(this.rootDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearView_IsExpanded ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (doc[Width]() || this.dimension()) + tot + 4, 0) : 0), width => this.childDocs.length && (this.layoutDoc._width = width), { fireImmediately: true } ); @@ -214,8 +214,8 @@ export class CollectionLinearView extends CollectionSubView() { docViewPath={returnEmptyDoclist} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} - docFilters={this.props.docFilters} - docRangeFilters={this.props.docRangeFilters} + childFilters={this.props.childFilters} + childFiltersByRanges={this.props.childFiltersByRanges} searchFilterDocs={this.props.searchFilterDocs} hideResizeHandles={true} /> @@ -226,7 +226,7 @@ export class CollectionLinearView extends CollectionSubView() { render() { const flexDir = StrCast(this.Document.flexDirection); // Specify direction of linear view content const flexGap = NumCast(this.Document.flexGap); // Specify the gap between linear view content - const isExpanded = BoolCast(this.layoutDoc.linearViewIsExpanded); + const isExpanded = BoolCast(this.layoutDoc.linearView_IsExpanded); const menuOpener = (