From d5f796b433d7e72130d4109a3775347ccb10c454 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 17 Jan 2023 22:34:32 -0500 Subject: fixed linkint to trail to follow trail immediately in lightbox and show trail ui in minimized mode. fixed overlay of pres box to not disappear when lightbox appears. closing /ending trail hackily restores collecftion to prior pan/zoom. --- src/client/views/OverlayView.tsx | 136 +++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 69 deletions(-) (limited to 'src/client/views/OverlayView.tsx') diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 6d7f2c037..08285ff0c 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -1,4 +1,4 @@ -import { action, computed, observable } from 'mobx'; +import { action, computed, observable, trace } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import * as React from 'react'; @@ -8,15 +8,17 @@ import { Id } from '../../fields/FieldSymbols'; import { NumCast } from '../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../Utils'; import { DocUtils } from '../documents/Documents'; +import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from '../util/DragManager'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; import { Transform } from '../util/Transform'; import { CollectionFreeFormLinksView } from './collections/collectionFreeForm/CollectionFreeFormLinksView'; import { LightboxView } from './LightboxView'; +import { MainView } from './MainView'; import { DocumentView } from './nodes/DocumentView'; import './OverlayView.scss'; import { ScriptingRepl } from './ScriptingRepl'; -import { DefaultStyleProvider } from './StyleProvider'; +import { DefaultStyleProvider, testDocProps } from './StyleProvider'; export type OverlayDisposer = () => void; @@ -170,74 +172,70 @@ export class OverlayView extends React.Component { ); @computed get overlayDocs() { - return LightboxView.LightboxDoc - ? null - : DocListCast(Doc.MyOverlayDocs?.data).map(d => { - let offsetx = 0, - offsety = 0; - const dref = React.createRef(); - const onPointerMove = action((e: PointerEvent, down: number[]) => { - if (e.buttons === 1) { - d.overlayX = e.clientX + offsetx; - d.overlayY = e.clientY + offsety; - } - if (e.metaKey) { - const dragData = new DragManager.DocumentDragData([d]); - dragData.offset = [-offsetx, -offsety]; - dragData.dropAction = 'move'; - dragData.removeDocument = (doc: Doc | Doc[]) => { - const docs = doc instanceof Doc ? [doc] : doc; - docs.forEach(d => Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, d)); - return true; - }; - dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => { - return dragData.removeDocument!(doc) ? addDocument(doc) : false; - }; - DragManager.StartDocumentDrag([dref.current!], dragData, down[0], down[1]); - return true; - } - return false; - }); + return DocListCast(Doc.MyOverlayDocs?.data) + .filter(d => !LightboxView.LightboxDoc || d.type === DocumentType.PRES) + .map(d => { + let offsetx = 0, + offsety = 0; + const dref = React.createRef(); + const onPointerMove = action((e: PointerEvent, down: number[]) => { + if (e.buttons === 1) { + d.overlayX = e.clientX + offsetx; + d.overlayY = e.clientY + offsety; + } + if (e.metaKey) { + const dragData = new DragManager.DocumentDragData([d]); + dragData.offset = [-offsetx, -offsety]; + dragData.dropAction = 'move'; + dragData.removeDocument = this.removeOverlayDoc; + dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => { + return dragData.removeDocument!(doc) ? addDocument(doc) : false; + }; + DragManager.StartDocumentDrag([dref.current!], dragData, down[0], down[1]); + return true; + } + return false; + }); - const onPointerDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, onPointerMove, emptyFunction, emptyFunction); - offsetx = NumCast(d.overlayX) - e.clientX; - offsety = NumCast(d.overlayY) - e.clientY; - }; - return ( -
- -
- ); - }); + const onPointerDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, onPointerMove, emptyFunction, emptyFunction); + offsetx = NumCast(d.overlayX) - e.clientX; + offsety = NumCast(d.overlayY) - e.clientY; + }; + return ( +
+ +
+ ); + }); } public static ShowSpinner() { -- cgit v1.2.3-70-g09d2 From 20c0190e820f2bd343693368b7ef55a91f19c880 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 14 Mar 2023 20:31:06 -0400 Subject: simplified Deserialize code. streamlined currentUserUtils to need fewer compiled functions by parameterizing more functions. . --- src/client/documents/Documents.ts | 13 +- src/client/util/CurrentUserUtils.ts | 145 +++++------ src/client/util/Scripting.ts | 4 +- src/client/util/SelectionManager.ts | 7 +- src/client/util/SerializationHelper.ts | 86 +------ src/client/views/DashboardView.tsx | 1 - src/client/views/GestureOverlay.tsx | 7 +- src/client/views/MainView.tsx | 20 -- src/client/views/OverlayView.tsx | 1 + .../views/collections/CollectionDockingView.tsx | 19 +- src/client/views/collections/CollectionView.tsx | 2 +- src/client/views/collections/TreeView.tsx | 10 +- src/client/views/nodes/DocumentView.tsx | 2 + src/client/views/nodes/WebBox.tsx | 8 +- src/client/views/nodes/button/FontIconBox.tsx | 272 +++++++-------------- src/fields/Doc.ts | 57 ++--- src/fields/ScriptField.ts | 31 +-- src/fields/util.ts | 22 +- 18 files changed, 253 insertions(+), 454 deletions(-) (limited to 'src/client/views/OverlayView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 251e432ef..457811e26 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -200,6 +200,8 @@ export class DocumentOptions { 'icon-nativeWidth'?: NUMt = new NumInfo('native width of icon view'); 'icon-nativeHeight'?: NUMt = new NumInfo('native height of icon view'); 'dragFactory-count'?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)'); + openFactoryLocation?: string; // an OpenWhere value to place the factory created document + openFactoryAsDelegate?: boolean; // lat?: number; lng?: number; infoWindowOpen?: boolean; @@ -220,6 +222,8 @@ export class DocumentOptions { 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. toolTip?: string; // tooltip to display on hover + toolType?: string; // type of pen tool + expertMode?: boolean; // something available only in expert (not novice) mode contextMenuFilters?: List; contextMenuScripts?: List; contextMenuLabels?: List; @@ -1353,6 +1357,8 @@ export namespace DocUtils { const script = scripts[key]; if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && script) { doc[key] = ScriptField.MakeScript(script, { + self: Doc.name, + this: Doc.name, dragData: DragManager.DocumentDragData.name, value: 'any', _readOnly_: 'boolean', @@ -1862,11 +1868,8 @@ export namespace DocUtils { } ScriptingGlobals.add('Docs', Docs); -ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { - return DocUtils.copyDragFactory(dragFactory); -}); -ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { - return DocUtils.delegateDragFactory(dragFactory); +ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc, asDelegate?: boolean) { + return dragFactory instanceof Doc ? (asDelegate ? DocUtils.delegateDragFactory(dragFactory) : DocUtils.copyDragFactory(dragFactory)) : dragFactory; }); ScriptingGlobals.add(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: 'child of ' + proto.title }); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index c4fb4788c..2820c66ee 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -21,6 +21,7 @@ import { DashboardView } from "../views/DashboardView"; import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; +import { OpenWhere } from "../views/nodes/DocumentView"; import { OverlayView } from "../views/OverlayView"; import { DragManager } from "./DragManager"; import { MakeTemplate } from "./DropConverter"; @@ -40,6 +41,8 @@ interface Button { numBtnMax?: number; switchToggle?: boolean; width?: number; + toolType?: string; // type of pen tool + expertMode?: boolean;// available only in expert mode btnList?: List; ignoreClick?: boolean; buttonText?: string; @@ -78,7 +81,7 @@ export class CurrentUserUtils { ]; const requiredTypes = requiredTypeNameFields.map(({ btnOpts, template, templateOpts }) => { const tempBtn = DocListCast(tempDocs?.data)?.find(doc => doc.title === btnOpts.title); - const reqdScripts = { onDragStart: '{ return copyDragFactory(this.dragFactory); }' }; + const reqdScripts = { onDragStart: '{ return copyDragFactory(this.dragFactory,this.openFactoryAsDelegate); }' }; const assignBtnAndTempOpts = (templateBtn:Opt, btnOpts:DocumentOptions, templateOptions:DocumentOptions) => { if (templateBtn) { DocUtils.AssignOpts(templateBtn,btnOpts); @@ -105,7 +108,7 @@ export class CurrentUserUtils { const reqdClickOpts:DocumentOptions = {_width: 300, _height:200, system: true}; const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [ { opts: { title: "Open In Target", targetScriptKey: "onChildClick"}, script: "docCast(thisContainer.target).then((target) => target && (target.proto.data = new List([self])))"}, - { opts: { title: "Open Detail On Right", targetScriptKey: "onChildDoubleClick"}, script: "openOnRight(self.doubleClickView)"}]; + { opts: { title: "Open Detail On Right", targetScriptKey: "onChildDoubleClick"}, script: `openDoc(self.doubleClickView.${OpenWhere.addRight})`}]; const reqdClickList = reqdTempOpts.map(opts => { const allOpts = {...reqdClickOpts, ...opts.opts}; const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(doc => doc.title === opts.opts.title): undefined; @@ -121,11 +124,11 @@ export class CurrentUserUtils { const tempClicks = DocCast(doc[field]); const reqdClickOpts:DocumentOptions = { _width: 300, _height:200, system: true}; const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [ - { opts: { title: "onClick"}, script: "console.log( 'click')"}, - { opts: { title: "onDoubleClick"}, script: "console.log( 'double click')"}, - { opts: { title: "onChildClick"}, script: "console.log( 'child click')"}, - { opts: { title: "onChildDoubleClick"}, script: "console.log( 'child double click')"}, - { opts: { title: "onCheckedClick"}, script: "console.log( heading, checked, containingTreeView)"}, + { opts: { title: "onClick"}, script: "console.log('click')"}, + { opts: { title: "onDoubleClick"}, script: "console.log('click')"}, + { opts: { title: "onChildClick"}, script: "console.log('click')"}, + { opts: { title: "onChildDoubleClick"}, script: "console.log('click')"}, + { opts: { title: "onCheckedClick"}, script: "console.log(heading, checked, containingTreeView)"}, ]; const reqdClickList = reqdTempOpts.map(opts => { const allOpts = {...reqdClickOpts, ...opts.opts}; @@ -183,7 +186,7 @@ export class CurrentUserUtils { const allopts = {system: true, ...opts}; return DocUtils.AssignScripts( (curIcon?.iconTemplate === opts.iconTemplate ? DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))), - {onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView"}); + {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 @@ -211,7 +214,7 @@ export class CurrentUserUtils { /// initalizes the set of "empty" versions of each document type with default fields. e.g.,. emptyNote, emptyTrail static creatorBtnDescriptors(doc: Doc): { title: string, toolTip: string, icon: string, ignoreClick?: boolean, dragFactory?: Doc, - backgroundColor?: string, clickFactory?: Doc, scripts?: { onClick?: string, onDragStart?: string}, funcs?: {onDragStart?:string, hidden?: string}, + backgroundColor?: string, openFactoryAsDelegate?:boolean, openFactoryLocation?:string, clickFactory?: Doc, scripts?: { onClick?: string, onDragStart?: string}, funcs?: {onDragStart?:string, hidden?: string}, }[] { const standardOps = (key:string) => ({ title : "Untitled "+ key, _fitWidth: false, system: true, "dragFactory-count": 0, cloneFieldFilter: new List(["system"]) }); const json = { @@ -257,13 +260,13 @@ export class CurrentUserUtils { 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, _autoHeight: true }}, - {key: "Noteboard", creator: opts => Docs.Create.NoteTakingDocument([], opts), opts: { _width: 250, _height: 200 }}, + {key: "Noteboard", creator: opts => Docs.Create.NoteTakingDocument([], opts), opts: { _width: 250, _height: 200, _fitWidth: true}}, {key: "Collection", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 150, _height: 100, _fitWidth: true }}, {key: "Equation", creator: opts => Docs.Create.EquationDocument(opts), opts: { _width: 300, _height: 35, _backgroundGridShow: true, }}, {key: "Webpage", creator: opts => Docs.Create.WebDocument("",opts), opts: { _width: 400, _height: 512, _nativeWidth: 850, useCors: true, }}, {key: "Comparison", creator: Docs.Create.ComparisonDocument, opts: { _width: 300, _height: 300 }}, {key: "Audio", creator: opts => Docs.Create.AudioDocument(nullAudio, opts),opts: { _width: 200, _height: 100, }}, - {key: "Map", creator: opts => Docs.Create.MapDocument([], opts), opts: { _width: 800, _height: 600, _showSidebar: true, }}, + {key: "Map", creator: opts => Docs.Create.MapDocument([], opts), opts: { _width: 800, _height: 600, _fitWidth: true, _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, system: true, cloneFieldFilter: new List(["system"]) }}, {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, _isLinkButton: true }}, @@ -282,30 +285,34 @@ export class CurrentUserUtils { emptyThings.forEach(thing => DocUtils.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, undefined, thing.funcs)); return [ - { toolTip: "Tap or drag to create a note", title: "Note", icon: "sticky-note", dragFactory: doc.emptyNote as Doc, }, - { toolTip: "Tap or drag to create a note board", title: "Notes", icon: "folder", dragFactory: doc.emptyNoteboard as Doc, }, - { toolTip: "Tap or drag to create a collection", title: "Col", icon: "folder", dragFactory: doc.emptyCollection as Doc, clickFactory: DocCast(doc.emptyTab), scripts: { onClick: 'openOnRight(copyDragFactory(this.clickFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, - { toolTip: "Tap or drag to create an equation", title: "Math", icon: "calculator", dragFactory: doc.emptyEquation as Doc, }, - { toolTip: "Tap or drag to create a webpage", title: "Web", icon: "globe-asia", dragFactory: doc.emptyWebpage as Doc, }, - { toolTip: "Tap or drag to create a comparison box", title: "Compare", icon: "columns", dragFactory: doc.emptyComparison as Doc, }, - { toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone", dragFactory: doc.emptyAudio as Doc, scripts: { onClick: 'openInOverlay(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, - { toolTip: "Tap or drag to create a map", title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc, }, - { toolTip: "Tap or drag to create a screen grabber", title: "Grab", icon: "photo-video", dragFactory: doc.emptyScreengrab as Doc, scripts: { onClick: 'openInOverlay(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'},funcs: { hidden: 'IsNoviceMode()'} }, - { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc, scripts: { onClick: 'openInOverlay(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'},funcs: { hidden: 'IsNoviceMode()'}}, - { toolTip: "Tap or drag to create a button", title: "Button", icon: "bolt", dragFactory: doc.emptyButton as Doc, funcs: { hidden: 'IsNoviceMode()'} }, - { toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, funcs: { hidden: 'IsNoviceMode()'}}, - { toolTip: "Tap or drag to create a data viz node", title: "DataViz", icon: "file", dragFactory: doc.emptyDataViz as Doc, }, - { toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "file", dragFactory: doc.emptySlide as Doc, funcs: { hidden: 'IsNoviceMode()'}}, - { toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize", dragFactory: doc.emptyHeader as Doc,scripts: { onClick: 'openOnRight(delegateDragFactory(this.dragFactory))', onDragStart: '{ return delegateDragFactory(this.dragFactory);}'}, }, - { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", scripts: { onClick: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' } }, - ].map(tuple => ({scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, ...tuple, })) + { toolTip: "Tap or drag to create a note", title: "Note", icon: "sticky-note", dragFactory: doc.emptyNote as Doc, clickFactory: DocCast(doc.emptyNote)}, + { toolTip: "Tap or drag to create a note board", title: "Notes", icon: "folder", dragFactory: doc.emptyNoteboard as Doc, clickFactory: DocCast(doc.emptyNoteboard)}, + { toolTip: "Tap or drag to create a collection", title: "Col", icon: "folder", dragFactory: doc.emptyCollection as Doc,clickFactory: DocCast(doc.emptyTab)}, + { toolTip: "Tap or drag to create an equation", title: "Math", icon: "calculator", dragFactory: doc.emptyEquation as Doc, clickFactory: DocCast(doc.emptyEquation)}, + { toolTip: "Tap or drag to create a webpage", title: "Web", icon: "globe-asia", dragFactory: doc.emptyWebpage as Doc, clickFactory: DocCast(doc.emptyWebpage)}, + { toolTip: "Tap or drag to create a comparison box", title: "Compare", icon: "columns", dragFactory: doc.emptyComparison as Doc,clickFactory: DocCast(doc.emptyComparison)}, + { toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone", dragFactory: doc.emptyAudio as Doc, clickFactory: DocCast(doc.emptyAudio), openFactoryLocation: OpenWhere.overlay}, + { toolTip: "Tap or drag to create a map", title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc, clickFactory: DocCast(doc.emptyMap)}, + { toolTip: "Tap or drag to create a screen grabber", title: "Grab", icon: "photo-video", dragFactory: doc.emptyScreengrab as Doc,clickFactory: DocCast(doc.emptyScreengrab), openFactoryLocation: OpenWhere.overlay}, + { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc, clickFactory: DocCast(doc.emptyWebCam), openFactoryLocation: OpenWhere.overlay}, + { toolTip: "Tap or drag to create a button", title: "Button", icon: "bolt", dragFactory: doc.emptyButton as Doc, clickFactory: DocCast(doc.emptyButton)}, + { toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, clickFactory: DocCast(doc.emptyScript)}, + { toolTip: "Tap or drag to create a data viz node", title: "DataViz", icon: "file", dragFactory: doc.emptyDataViz as Doc, clickFactory: DocCast(doc.emptyDataViz)}, + { toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "file", dragFactory: doc.emptySlide as Doc, clickFactory: DocCast(doc.emptySlide), openFactoryLocation: OpenWhere.overlay}, + { toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize", dragFactory: doc.emptyHeader as Doc, clickFactory: DocCast(doc.emptyHeader), openFactoryAsDelegate: true }, + { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", clickFactory: "repl" as any, openFactoryLocation: OpenWhere.overlay}, + ].map(tuple => ( + { openFactoryLocation: OpenWhere.addRight, + scripts: { onClick: 'openDoc(copyDragFactory(this.clickFactory,this.openFactoryAsDelegate), this.openFactoryLocation)', + onDragStart: '{ return copyDragFactory(this.dragFactory,this.openFactoryAsDelegate); }'}, + ...tuple, })) } /// Initalizes the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools static setupCreatorButtons(doc: Doc, dragCreatorDoc?:Doc):Doc { 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, + const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit, _nativeWidth: 50, _nativeHeight: 50, _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, system: true, _removeDropProperties: new List(["_stayInCollection"]), @@ -603,53 +610,50 @@ export class CurrentUserUtils { static textTools():Button[] { return [ - { title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, scripts: {script: 'setFont(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", width: 75, btnType: ButtonType.NumberButton, ignoreClick: true, scripts: {script: '{ return setFontSize(value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions }, - { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, scripts: {script: '{ return setFontColor(value, _readOnly_);}'}}, - { title: "Highlight",toolTip:"Font highlight", btnType: ButtonType.ColorButton, icon: "highlighter", ignoreClick: true, scripts: {script: '{ return setFontHighlight(value, _readOnly_);}'},funcs: {hidden: "IsNoviceMode()"} }, - { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", scripts: {onClick: '{ return toggleBold(_readOnly_); }'} }, - { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", scripts: {onClick: '{ return toggleItalic(_readOnly_);}'} }, - { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", scripts: {onClick: '{ return toggleUnderline(_readOnly_);}'} }, - { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", scripts: {onClick: '{ return setBulletList("bullet", _readOnly_);}'} }, - { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", scripts: {onClick: '{ return setBulletList("decimal", _readOnly_);}'} }, - + { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, toolType:"fontSize", ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions }, + { title: "Color", toolTip: "Font 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", btnType: ButtonType.ToggleButton, icon: "align-left", toolType:"left", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }}, + { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, + { title: "Right", toolTip: "Right align", 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()'}}, - { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", scripts: {onClick:'{ return setAlignment("left", _readOnly_);}' }}, - { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", scripts: {onClick:'{ return setAlignment("center", _readOnly_);}'} }, - { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", scripts: {onClick:'{ return setAlignment("right", _readOnly_);}'} }, - { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", scripts: {onClick:'{ return toggleNoAutoLinkAnchor(_readOnly_);}'}, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}}, - { title: "Dictate",toolTip: "Dictate", btnType: ButtonType.ToggleButton, icon: "microphone", scripts: {onClick:'{ return toggleDictation(_readOnly_);}'}}, - ]; + ]; } static inkTools():Button[] { return [ - { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", scripts: {onClick:'{ return setActiveTool("pen", false, _readOnly_);}' }}, - { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", scripts: {onClick:'{ return setActiveTool("write", false, _readOnly_);}'} }, - { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", scripts: {onClick:'{ return setActiveTool("eraser", false, _readOnly_);}' }}, - // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveTool("highlighter")'} }, - { title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle", scripts: {onClick:`{ return setActiveTool("${GestureUtils.Gestures.Circle}", false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool("${GestureUtils.Gestures.Circle}", true, _readOnly_);}`} }, - { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square", scripts: {onClick:`{ return setActiveTool("${GestureUtils.Gestures.Rectangle}", false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool("${GestureUtils.Gestures.Rectangle}", true, _readOnly_);}`} }, - { title: "Line", toolTip: "Line (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "minus", scripts: {onClick:`{ return setActiveTool("${GestureUtils.Gestures.Line}", false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool("${GestureUtils.Gestures.Line}", true, _readOnly_);}`} }, - { title: "Mask", toolTip: "Mask", btnType: ButtonType.ToggleButton, icon: "user-circle", scripts: {onClick:'{ return setIsInkMask(_readOnly_);}'} }, - { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, icon: "fill-drip",ignoreClick: true, scripts: {script: '{ return setFillColor(value, _readOnly_);}'} }, - { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, ignoreClick: true, scripts: {script: '{ return setStrokeWidth(value, _readOnly_);}'}, numBtnType: NumButtonType.Slider, numBtnMin: 1}, - { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, scripts: {script: '{ return setStrokeColor(value, _readOnly_);}'} }, + { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", toolType: "pen", scripts: {onClick:'{ return setActiveTool(self.toolType, false, _readOnly_);}' }}, + { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", toolType: "write", scripts: {onClick:'{ return setActiveTool(self.toolType, false, _readOnly_);}'} }, + { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", toolType: "eraser", scripts: {onClick:'{ return setActiveTool(self.toolType, false, _readOnly_);}' }}, + { title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle", toolType:GestureUtils.Gestures.Circle, scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} }, + { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square", toolType:GestureUtils.Gestures.Rectangle, scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} }, + { title: "Line", toolTip: "Line (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "minus", toolType:GestureUtils.Gestures.Line, scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} }, + { title: "Mask", toolTip: "Mask", btnType: ButtonType.ToggleButton, icon: "user-circle",toolType: "inkMask", scripts: {onClick:'{ return setInkProperty(self.toolType, value, _readOnly_);}'} }, + { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, icon: "fill-drip", toolType: "fillColor", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'} }, + { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'}, numBtnType: NumButtonType.Slider, numBtnMin: 1}, + { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", toolType: "strokeColor", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'} }, ]; } static schemaTools():Button[] { - return [{ title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", scripts:{ onClick: '{return toggleSchemaPreview(_readOnly_);}'}, }]; + return [{ title: "Show preview", toolTip: "Show selection preview", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", scripts:{ onClick: '{ return toggleSchemaPreview(_readOnly_); }'}, }]; } static webTools() { return [ { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", scripts: { onClick: '{ return webBack(_readOnly_); }' }}, { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", scripts: { onClick: '{ return webForward(_readOnly_); }'}}, - //{ title: "Reload", toolTip: "Reload webpage", btnType: ButtonType.ClickButton, icon: "redo-alt", click: 'webReload()' }, { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, scripts: { script: '{ return webSetURL(value, _readOnly_); }'} }, ]; } @@ -661,18 +665,18 @@ export class CurrentUserUtils { CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel, CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, CollectionViewType.Grid, CollectionViewType.NoteTaking]), - title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}}, - { title: "Pin", icon: "map-pin", toolTip: "Pin View to Trail", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "tab")'}, width: 20, scripts: { onClick: 'pinWithView(_readOnly_, altKey)'}}, - { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}}, - { title: "Num",icon: "",toolTip: "Frame Number (click to toggle edit mode)",btnType: ButtonType.TextButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}}, - { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}}, - { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",btnType: ButtonType.ColorButton, funcs: {hidden: '!SelectionManager_selectedDocType()'}, ignoreClick: true, width: 20, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected - { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, funcs: {hidden: '!SelectionManager_selectedDocType()'}, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'}}, - { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform", true)'}, scripts: { onClick: 'toggleOverlay(_readOnly_)'}}, // Only when floating document is selected in freeform - { title: "Text", icon: "Text", toolTip: "Text functions", subMenu: CurrentUserUtils.textTools(), funcs: {hidden: 'false', linearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.RTF}")`} }, // Always available - { title: "Ink", icon: "Ink", toolTip: "Ink functions", subMenu: CurrentUserUtils.inkTools(), funcs: {hidden: 'false', linearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.INK}")`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available - { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), funcs: {hidden: `!SelectionManager_selectedDocType("${DocumentType.WEB}")`, linearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.WEB}")`, } }, // Only when Web is selected - { title: "Schema", icon: "Schema", toolTip: "Schema functions", subMenu: CurrentUserUtils.schemaTools(), funcs: {hidden: `!SelectionManager_selectedDocType(undefined, "${CollectionViewType.Schema}")`, linearViewIsExpanded: `SelectionManager_selectedDocType(undefined, "${CollectionViewType.Schema}")`} } // Only when Schema is selected + title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}}, + { title: "Pin", icon: "map-pin", toolTip: "Pin View to Trail", btnType: ButtonType.ClickButton, expertMode: false, toolType:"tab", funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`}, width: 20, scripts: { onClick: 'pinWithView(_readOnly_, altKey)'}}, + { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",btnType: ButtonType.ColorButton, expertMode: false, toolType:"tab", ignoreClick: true, width: 20, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected + { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, expertMode: false, toolType:"tab", ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'}}, + { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'}, scripts: { onClick: 'toggleOverlay(_readOnly_)'}}, // Only when floating document is selected in freeform + { 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: "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", 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 ]; } @@ -684,6 +688,7 @@ export class CurrentUserUtils { color: Colors.WHITE, system: true, dontUndo: true, _nativeWidth: params.width ?? 30, _width: params.width ?? 30, _height: 30, _nativeHeight: 30, + toolType: params.toolType, expertMode: params.expertMode, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, _removeDropProperties: new List([ "_stayInCollection"]), }; diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 6dcdcb71b..d32298c83 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -248,8 +248,10 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp ScriptingGlobals.resetScriptingGlobals(); } !signature.includes('XXX') && ScriptField._scriptFieldCache.set(script + ':' + signature, result as CompiledScript); - //console.log('COMPILED: ' + script + ':' + signature); return result; } ScriptingGlobals.add(CompileScript); +ScriptingGlobals.add(function runScript(self: Doc, script: ScriptField) { + return script?.script.run({ this: self, self: self }).result; +}); diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index c0fc25376..0f4f77588 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -104,10 +104,11 @@ export namespace SelectionManager { return Array.from(manager.SelectedViews.values()).filter(doc => doc?._viewType !== CollectionViewType.Docking); } } -ScriptingGlobals.add(function SelectionManager_selectedDocType(docType?: DocumentType, colType?: CollectionViewType, checkContext?: boolean) { - if (colType === ('tab' as any)) { +ScriptingGlobals.add(function SelectionManager_selectedDocType(type: string, expertMode: boolean, checkContext?: boolean) { + if (Doc.noviceMode && expertMode) return false; + if (type === 'tab') { return SelectionManager.Views().lastElement()?.props.renderDepth === 0; } let selected = (sel => (checkContext ? DocCast(sel?.context) : sel))(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement()); - return docType ? selected?.type === docType : colType ? selected?.viewType === colType : true; + return selected?.type === type || selected?.viewType === type || !type; }); diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts index 2d1f61cfb..76037a7e9 100644 --- a/src/client/util/SerializationHelper.ts +++ b/src/client/util/SerializationHelper.ts @@ -44,12 +44,8 @@ export namespace SerializationHelper { } if (!obj.__type) { - if (true || ClientUtils.RELEASE) { - console.warn("No property 'type' found in JSON."); - return undefined; - } else { - throw Error("No property 'type' found in JSON."); - } + console.warn("No property 'type' found in JSON."); + return undefined; } if (!(obj.__type in serializationTypes)) { @@ -58,9 +54,8 @@ export namespace SerializationHelper { const type = serializationTypes[obj.__type]; const value = await new Promise(res => deserialize(type.ctor, obj, (err, result) => res(result))); - if (type.afterDeserialize) { - type.afterDeserialize(value); - } + type.afterDeserialize?.(value); + return value; } } @@ -68,75 +63,20 @@ export namespace SerializationHelper { const serializationTypes: { [name: string]: { ctor: { new (): any }; afterDeserialize?: (obj: any) => void | Promise } } = {}; const reverseMap: { [ctor: string]: string } = {}; -export interface DeserializableOpts { - (constructor: { new (...args: any[]): any }): void; - withFields(fields: string[]): Function; -} - -export function Deserializable(name: string, afterDeserialize?: (obj: any) => void | Promise): DeserializableOpts; -export function Deserializable(constructor: { new (...args: any[]): any }): void; -export function Deserializable(constructor: { new (...args: any[]): any } | string, afterDeserialize?: (obj: any) => void): DeserializableOpts | void { - function addToMap(name: string, ctor: { new (...args: any[]): any }) { +export function Deserializable(className: string, afterDeserialize?: (obj: any) => void | Promise, constructorArgs?: [string]): (constructor: { new (...args: any[]): any }) => void { + function addToMap(className: string, ctor: { new (...args: any[]): any }) { const schema = getDefaultModelSchema(ctor) as any; - if (schema.targetClass !== ctor) { - const newSchema = { ...schema, factory: () => new ctor() }; - setDefaultModelSchema(ctor, newSchema); + if (schema.targetClass !== ctor || constructorArgs) { + setDefaultModelSchema(ctor, { ...schema, factory: (context: any) => new ctor(...(constructorArgs ?? [])?.map(arg => context.json[arg])) }); } - if (!(name in serializationTypes)) { - serializationTypes[name] = { ctor, afterDeserialize }; - reverseMap[ctor.name] = name; + if (!(className in serializationTypes)) { + serializationTypes[className] = { ctor, afterDeserialize }; + reverseMap[ctor.name] = className; } else { - throw new Error(`Name ${name} has already been registered as deserializable`); + throw new Error(`Name ${className} has already been registered as deserializable`); } } - if (typeof constructor === 'string') { - return Object.assign( - (ctor: { new (...args: any[]): any }) => { - addToMap(constructor, ctor); - }, - { withFields: (fields: string[]) => Deserializable.withFields(fields, constructor, afterDeserialize) } - ); - } - addToMap(constructor.name, constructor); -} - -export namespace Deserializable { - export function withFields(fields: string[], name?: string, afterDeserialize?: (obj: any) => void | Promise) { - return function (constructor: { new (...fields: any[]): any }) { - Deserializable(name || constructor.name, afterDeserialize)(constructor); - let schema = getDefaultModelSchema(constructor); - if (schema) { - schema.factory = context => { - const args = fields.map(key => context.json[key]); - return new constructor(...args); - }; - // TODO A modified version of this would let us not reassign fields that we're passing into the constructor later on in deserializing - // fields.forEach(field => { - // if (field in schema.props) { - // let propSchema = schema.props[field]; - // if (propSchema === false) { - // return; - // } else if (propSchema === true) { - // propSchema = primitive(); - // } - // schema.props[field] = custom(propSchema.serializer, - // () => { - // return SKIP; - // }); - // } - // }); - } else { - schema = { - props: {}, - factory: context => { - const args = fields.map(key => context.json[key]); - return new constructor(...args); - }, - }; - setDefaultModelSchema(constructor, schema); - } - }; - } + return (ctor: { new (...args: any[]): any }) => addToMap(className, ctor); } export function autoObject(): PropSchema { diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 7ebe8d0e3..2b586b0e2 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -233,7 +233,6 @@ export class DashboardView extends React.Component { /// this also sets the readonly state of the dashboard based on the current mode of dash (from its url) public static openDashboard = (doc: Doc | undefined, fromHistory = false) => { if (!doc) return false; - Doc.MainDocId = doc[Id]; Doc.AddDocToList(Doc.MyDashboards, 'data', doc); // this has the side-effect of setting the main container since we're assigning the active/guest dashboard diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 6058eaaf9..0feccb742 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -94,7 +94,7 @@ export class GestureOverlay extends Touchable { } static setupThumbButtons(doc: Doc) { - const docProtoData: { title: string; icon: string; drag?: string; ignoreClick?: boolean; pointerDown?: string; pointerUp?: string; clipboard?: Doc; backgroundColor?: string; dragFactory?: Doc }[] = [ + const docProtoData: { title: string; icon: string; drag?: string; toolType?: string; ignoreClick?: boolean; pointerDown?: string; pointerUp?: string; clipboard?: Doc; backgroundColor?: string; dragFactory?: Doc }[] = [ { title: 'use pen', icon: 'pen-nib', pointerUp: 'resetPen()', pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: 'blue' }, { title: 'use highlighter', icon: 'highlighter', pointerUp: 'resetPen()', pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: 'yellow' }, { @@ -105,8 +105,8 @@ export class GestureOverlay extends Touchable { clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, system: true }), backgroundColor: 'orange', }, - { title: 'interpret text', icon: 'font', pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: 'orange' }, - { title: 'ignore gestures', icon: 'signature', pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: 'green' }, + { title: 'interpret text', icon: 'font', toolType: 'inktotext', pointerUp: "setToolglass('none')", pointerDown: 'setToolglass(self.toolType)', backgroundColor: 'orange' }, + { title: 'ignore gestures', icon: 'signature', toolType: 'ignoregesture', pointerUp: "setToolglass('none')", pointerDown: 'setToolglass(self.toolType)', backgroundColor: 'green' }, ]; return docProtoData.map(data => Docs.Create.FontIconDocument({ @@ -116,6 +116,7 @@ export class GestureOverlay extends Touchable { _height: 10, title: data.title, icon: data.icon, + toolType: data.toolType, _dropAction: data.pointerDown ? 'copy' : undefined, ignoreClick: data.ignoreClick, onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 945cd61db..2e04ca3dd 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -230,8 +230,6 @@ export class MainView extends React.Component { if (window.location.pathname !== '/home') { const pathname = window.location.pathname.substr(1).split('/'); if (pathname.length > 1 && pathname[0] === 'doc') { - Doc.MainDocId = pathname[1]; - //!this.userDoc && DocServer.GetRefField(pathname[1]).then( action(field => { if (field instanceof Doc && field._viewType !== CollectionViewType.Docking) { @@ -482,7 +480,6 @@ export class MainView extends React.Component { fa.faHandPointUp, ] ); - this.initAuthenticationRouters(); } globalPointerDown = action((e: PointerEvent) => { @@ -521,23 +518,6 @@ export class MainView extends React.Component { document.oncontextmenu = () => false; }; - initAuthenticationRouters = async () => { - const received = Doc.MainDocId; - if (received && !this.userDoc) { - reaction( - () => Doc.GuestTarget, - target => target && DashboardView.createNewDashboard(), - { fireImmediately: true } - ); - } - // else { - // PromiseValue(this.userDoc.activeDashboard).then(dash => { - // if (dash instanceof Doc) DashboardView.openDashboard(dash); - // else Doc.createNewDashboard(); - // }); - // } - }; - @action createNewPresentation = () => { const pres = Doc.MakeCopy(Doc.UserDoc().emptyTrail as Doc, true); diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 08285ff0c..34e8cd6dd 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -255,4 +255,5 @@ export class OverlayView extends React.Component { // bcz: ugh ... want to be able to pass ScriptingRepl as tag argument, but that doesn't seem to work.. runtime error ScriptingGlobals.add(function addOverlayWindow(type: string, options: OverlayElementOptions) { OverlayView.Instance.addWindow(, options); + addOverlayWindow('ScriptingRepl', { x: 300, y: 100, width: 200, height: 200, title: 'Scripting REPL' }); }); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 1ead80bd0..9b6554d67 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -26,6 +26,9 @@ 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'; +import { ScriptField } from '../../../fields/ScriptField'; const _global = (window /* browser */ || global) /* node */ as any; @observer @@ -580,15 +583,21 @@ ScriptingGlobals.add( '(doc: any)' ); ScriptingGlobals.add( - function openOnRight(doc: any) { - return CollectionDockingView.AddSplit(doc, OpenWhereMod.right); + function openDoc(doc: any, where: OpenWhere) { + switch (where) { + case OpenWhere.addRight: + return CollectionDockingView.AddSplit(doc, OpenWhereMod.right); + case OpenWhere.overlay: + if (doc === 'repl') OverlayView.Instance.addWindow(, { x: 300, y: 100, width: 200, height: 200, title: 'Scripting REPL' }); + else Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc); + } }, - 'opens up document in tab on right side of the screen', + 'opens up document in location specified', '(doc: any)' ); ScriptingGlobals.add( - function openInOverlay(doc: any) { - return Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc); + function openRepl() { + return 'openRepl'; }, 'opens up document in screen overlay layer', '(doc: any)' diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 48e5748a0..aed88aa1a 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -169,7 +169,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent { const cm = ContextMenu.Instance; - if (cm && !e.isPropagationStopped() && this.rootDoc[Id] !== Doc.MainDocId) { + if (cm && !e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 this.setupViewTypes( 'UI Controls...', diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 2bdcf472f..af2d148e0 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; -import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc'; +import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { RichTextField } from '../../../fields/RichTextField'; @@ -172,11 +172,7 @@ export class TreeView extends React.Component { childDocList(field: string) { const layout = Cast(Doc.LayoutField(this.doc), Doc, null); - return ( - (this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field - (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields - DocListCastOrNull(this.doc[field]) - ); // otherwise use the document's data field + return DocListCast(this.props.dataDoc?.[field], DocListCast(layout?.[field], DocListCast(this.doc[field]))); } moving: boolean = false; @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => { @@ -759,7 +755,7 @@ export class TreeView extends React.Component { const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'New Folder' }; const deleteItem = { script: ScriptField.MakeFunction(`scriptContext.deleteItem()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'Delete' }; const folderOp = this.childDocs?.length ? [makeFolder] : []; - const openAlias = { script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, icon: 'copy', label: 'Open Alias' }; + const openAlias = { script: ScriptField.MakeFunction(`openDoc(getAlias(self), ${OpenWhere.addRight})`)!, icon: 'copy', label: 'Open Alias' }; const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Focus or Open' }; return [ ...(this.props.contextMenuItems ?? []).filter(mi => (!mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result)), diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 964231e9d..02af30d0c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -80,6 +80,7 @@ export enum OpenWhere { replaceLeft = 'replace:left', inParent = 'inParent', inParentFromScreen = 'inParentFromScreen', + overlay = 'overlay', } export enum OpenWhereMod { none = '', @@ -612,6 +613,7 @@ export class DocumentViewInternal extends DocComponent this.props.isSelected(true) || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document), async selected => { if (selected) { + this._thumbTimer && clearTimeout(this._thumbTimer); this._webPageHasBeenRendered = true; } else if ( (!this.props.isContentActive(true) || SnappingManager.GetIsDragging()) && // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail) @@ -204,7 +209,8 @@ export class WebBox extends ViewBoxAnnotatableComponent() { @computed get numberButton() { const numBtnType: string = StrCast(this.rootDoc.numBtnType); const numScript = ScriptCast(this.rootDoc.script); - const setValue = (value: number) => UndoManager.RunInBatch(() => numScript?.script.run({ value, _readOnly_: false }), 'set num value'); + 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({ value: 0, _readOnly_: true }).result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)); + const checkResult = Number(numScript?.script.run({ self: this.rootDoc, value: 0, _readOnly_: true }).result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)); const label = !FontIconBox.GetShowLabels() ? null :
{this.label}
; @@ -150,7 +151,7 @@ export class FontIconBox extends DocComponent() { min={NumCast(this.rootDoc.numBtnMin, 0)} max={NumCast(this.rootDoc.numBtnMax, 100)} value={checkResult} - className={'menu-slider'} + className="menu-slider" onPointerDown={() => (this._batch = UndoManager.StartBatch('presDuration'))} onPointerUp={() => this._batch?.end()} onChange={e => { @@ -284,27 +285,23 @@ export class FontIconBox extends DocComponent() { text = 'User Default'; } noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.NoteTaking]; - } else if (script?.script.originalScript.startsWith('setFont')) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; - text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); - noviceList = ['Roboto', 'Times New Roman', 'Arial', 'Georgia', 'Comic Sans MS', 'Tahoma', 'Impact', 'Crimson Text']; - } + } else text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); } catch (e) { console.log(e); } // Get items to place into the list const list = this.buttonList - .filter(value => !Doc.noviceMode || noviceList.includes(value)) + .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value)) .map(value => (
script.script.run({ value }))}> + onClick={undoBatch(() => script.script.run({ self: this.rootDoc, value }))}> {value[0].toUpperCase() + value.slice(1)}
)); @@ -357,7 +354,7 @@ export class FontIconBox extends DocComponent() { ev.preventDefault(); ev.stopPropagation(); const s = this.colorScript; - s && undoBatch(() => s.script.run({ value: Utils.colorString(value), _readOnly_: false }).result)(); + s && undoBatch(() => s.script.run({ self: this.rootDoc, value: Utils.colorString(value), _readOnly_: false }).result)(); }; const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']; return ; @@ -368,7 +365,7 @@ export class FontIconBox extends DocComponent() { @computed get colorButton() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? 'transparent'; + const curColor = this.colorScript?.script.run({ self: this.rootDoc, value: undefined, _readOnly_: true }).result ?? 'transparent'; const label = !this.label || !FontIconBox.GetShowLabels() ? null : ( @@ -377,13 +374,6 @@ export class FontIconBox extends DocComponent() { ); - // dropdown caret seems superfluous since clicking the color button does the same thing - // const dropdownCaret =
- // - //
; - //setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView return (
any; setDoc: () => void; setMode?: () => void }> = new Map([ + ['font', { + checkResult: () => RichTextMenu.Instance?.fontFamily, + setDoc: () => value && RichTextMenu.Instance.setFontFamily(value), + setMode: () => Doc.UserDoc().textAlign = value, + }], + ['highlight', { + checkResult: () =>(selected ?? Doc.UserDoc())._fontHighlight, + setDoc: () => value && RichTextMenu.Instance.setHighlight(value), + }], + ['fontColor', { + checkResult: () => RichTextMenu.Instance?.fontColor, + setDoc: () => value && RichTextMenu.Instance.setColor(value), + }], + ['fontSize', { + checkResult: () => RichTextMenu.Instance?.fontSize.replace('px', ''), + setDoc: () => { + if (typeof value === 'number') value = value.toString(); + if (value && Number(value).toString() === value) value += 'px'; + RichTextMenu.Instance.setFontSize(value); + }, + }], + ]); if (checkResult) { - return (selected ?? Doc.UserDoc())._fontHighlight; + return map.get(attr)?.checkResult(); } - color && RichTextMenu.Instance.setHighlight(color); + if (editorView?.state) map.get(attr)?.setDoc(); + else map.get(attr)?.setMode?.(); }); -// toggle: Set overlay status of selected document -ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: boolean) { - if (checkResult) { - return RichTextMenu.Instance?.fontSize.replace('px', ''); - } - if (typeof size === 'number') size = size.toString(); - if (size && Number(size).toString() === size) size += 'px'; - RichTextMenu.Instance.setFontSize(size); -}); -ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; - if (checkResult) { - return (editorView ? RichTextMenu.Instance.noAutoLink : false) ? Colors.MEDIUM_BLUE : 'transparent'; - } - if (editorView) RichTextMenu.Instance?.toggleNoAutoLinkAnchor(); -}); -ScriptingGlobals.add(function toggleDictation(checkResult?: boolean) { +type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'underline' | 'left' | 'center' | 'right' | 'bullet' | 'decimal'; +type attrfuncs = [attrname, { checkResult: () => boolean; toggle: () => any }]; +ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?: boolean) { const textView = RichTextMenu.Instance?.TextView; - if (checkResult) { - return textView?._recording ? Colors.MEDIUM_BLUE : 'transparent'; - } - if (textView) runInAction(() => (textView._recording = !textView._recording)); -}); - -ScriptingGlobals.add(function toggleBold(checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; - if (checkResult) { - return (editorView ? RichTextMenu.Instance.bold : Doc.UserDoc().fontWeight === 'bold') ? Colors.MEDIUM_BLUE : 'transparent'; - } - if (editorView) RichTextMenu.Instance?.toggleBold(); - else Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === 'bold' ? undefined : 'bold'; -}); - -ScriptingGlobals.add(function toggleUnderline(checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; - if (checkResult) { - return (editorView ? RichTextMenu.Instance.underline : Doc.UserDoc().textDecoration === 'underline') ? Colors.MEDIUM_BLUE : 'transparent'; - } - if (editorView) RichTextMenu.Instance?.toggleUnderline(); - else Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === 'underline' ? undefined : 'underline'; -}); - -ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; - if (checkResult) { - return (editorView ? RichTextMenu.Instance.italics : Doc.UserDoc().fontStyle === 'italics') ? Colors.MEDIUM_BLUE : 'transparent'; - } - if (editorView) RichTextMenu.Instance?.toggleItalics(); - else Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === 'italics' ? undefined : 'italics'; + const editorView = textView?.EditorView; + // prettier-ignore + const alignments:attrfuncs[] = (['left','right','center'] as ("left"|"center"|"right")[]).map((where) => + [ where, { checkResult: () =>(editorView ? (RichTextMenu.Instance.textAlign ===where): (Doc.UserDoc().textAlign ===where) ? true:false), + toggle: () => (editorView?.state ? RichTextMenu.Instance.align(editorView, editorView.dispatch, where):(Doc.UserDoc().textAlign = where))}]); + // prettier-ignore + const listings:attrfuncs[] = (['bullet','decimal'] as attrname[]).map(list => + [ list, { checkResult: () => (editorView ? RichTextMenu.Instance.getActiveListStyle() === list:false), + toggle: () => editorView?.state && RichTextMenu.Instance.changeListType(list) }]); + // prettier-ignore + const attrs:attrfuncs[] = [ + ['dictation', { checkResult: () => textView?._recording ? true:false, + toggle: () => textView && runInAction(() => (textView._recording = !textView._recording)) }], + ['noAutoLink',{ checkResult: () => (editorView ? RichTextMenu.Instance.noAutoLink : false), + toggle: () => editorView && RichTextMenu.Instance?.toggleNoAutoLinkAnchor()}], + ['bold', { checkResult: () => (editorView ? RichTextMenu.Instance.bold : (Doc.UserDoc().fontWeight === 'bold') ? true:false), + toggle: editorView ? RichTextMenu.Instance.toggleBold : () => (Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === 'bold' ? undefined : 'bold')}], + ['italics', { checkResult: () => (editorView ? RichTextMenu.Instance.italics : (Doc.UserDoc().fontStyle === 'italics') ? true:false), + toggle: editorView ? RichTextMenu.Instance.toggleItalics : () => (Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === 'italics' ? undefined : 'italics')}], + ['underline', { checkResult: () => (editorView ? RichTextMenu.Instance.underline : (Doc.UserDoc().textDecoration === 'underline') ? true:false), + toggle: editorView ? RichTextMenu.Instance.toggleUnderline : () => (Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === 'underline' ? undefined : 'underline') }]] + + const map = new Map(attrs.concat(alignments).concat(listings)); + if (checkResult) return map.get(charStyle)?.checkResult() ? Colors.MEDIUM_BLUE : 'transparent'; + map.get(charStyle)?.toggle(); }); export function checkInksToGroup() { @@ -832,62 +771,39 @@ function setActiveTool(tool: InkTool | GestureUtils.Gestures, keepPrim: boolean, ScriptingGlobals.add(setActiveTool, 'sets the active ink tool mode'); // toggle: Set overlay status of selected document -ScriptingGlobals.add(function setIsInkMask(checkResult?: boolean) { - const selected = SelectionManager.Docs().lastElement(); - if (checkResult) { - if (selected?.type === DocumentType.INK) { - return BoolCast(selected.isInkMask) ? Colors.MEDIUM_BLUE : 'transparent'; - } - return ActiveIsInkMask() ? Colors.MEDIUM_BLUE : 'transparent'; - } - SetActiveIsInkMask(!ActiveIsInkMask()); - SelectionManager.Docs() - .filter(doc => doc.type === DocumentType.INK) - .map(doc => (doc.isInkMask = !doc.isInkMask)); -}); - -// toggle: Set overlay status of selected document -ScriptingGlobals.add(function setFillColor(color?: string, checkResult?: boolean) { +ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', value: any, checkResult?: boolean) { const selected = SelectionManager.Docs().lastElement(); - if (checkResult) { - if (selected?.type === DocumentType.INK) { - return StrCast(selected.fillColor); - } - return ActiveFillColor(); - } - SetActiveFillColor(StrCast(color)); - SelectionManager.Docs() - .filter(doc => doc.type === DocumentType.INK) - .map(doc => (doc.fillColor = color)); -}); - -ScriptingGlobals.add(function setStrokeWidth(width: number, checkResult?: boolean) { - if (checkResult) { - const selected = SelectionManager.Docs().lastElement(); - if (selected?.type === DocumentType.INK) { - return NumCast(selected.strokeWidth); - } - return ActiveInkWidth(); - } - SetActiveInkWidth(width.toString()); - SelectionManager.Docs() - .filter(doc => doc.type === DocumentType.INK) - .map(doc => (doc.strokeWidth = Number(width))); -}); + // prettier-ignore + const map: Map<'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', { checkResult: () => any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([ + ['inkMask', { + checkResult: () => ((selected?.type === DocumentType.INK ? BoolCast(selected.isInkMask) : ActiveIsInkMask()) ? Colors.MEDIUM_BLUE : 'transparent'), + setInk: (doc: Doc) => (doc.isInkMask = !doc.isInkMask), + setMode: () => selected?.type !== DocumentType.INK && SetActiveIsInkMask(!ActiveIsInkMask()), + }], + ['fillColor', { + checkResult: () => (selected?.type === DocumentType.INK ? StrCast(selected.fillColor) : ActiveFillColor() ? Colors.MEDIUM_BLUE : 'transparent'), + setInk: (doc: Doc) => (doc.fillColor = StrCast(value)), + setMode: () => SetActiveFillColor(StrCast(value)), + }], + [ 'strokeWidth', { + checkResult: () => (selected?.type === DocumentType.INK ? NumCast(selected.strokeWidth) : ActiveInkWidth()), + setInk: (doc: Doc) => (doc.strokeWidth = NumCast(value)), + setMode: () => SetActiveInkWidth(value.toString()), + }], + ['strokeColor', { + checkResult: () => (selected?.type === DocumentType.INK ? NumCast(selected.color) : ActiveInkColor()), + setInk: (doc: Doc) => (doc.color = String(value)), + setMode: () => SetActiveInkColor(StrCast(value)), + }], + ]); -// toggle: Set overlay status of selected document -ScriptingGlobals.add(function setStrokeColor(color?: string, checkResult?: boolean) { if (checkResult) { - const selected = SelectionManager.Docs().lastElement(); - if (selected?.type === DocumentType.INK) { - return StrCast(selected.color); - } - return ActiveInkColor(); + return map.get(option)?.checkResult(); } - SetActiveInkColor(StrCast(color)); + map.get(option)?.setMode(); SelectionManager.Docs() .filter(doc => doc.type === DocumentType.INK) - .map(doc => (doc.color = String(color))); + .map(doc => map.get(option)?.setInk(doc)); }); /** WEB diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index ee164ab31..de94ed5db 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -33,20 +33,19 @@ export namespace Field { return !Field.IsField(field) ? '' : (onDelegate ? '=' : '') + (field instanceof ComputedField ? `:=${field.script.originalScript}` : Field.toScriptString(field)); } export function toScriptString(field: Field): string { - if (typeof field === 'string') { - if (field.startsWith('{"')) return `'${field}'`; // bcz: hack ... want to quote the string the right way. if there are nested "'s, then use ' instead of ". In this case, test for the start of a JSON string of the format {"property": ... } and use outer 's instead of "s - return `"${field}"`; + switch (typeof field) { + case 'string': + if (field.startsWith('{"')) return `'${field}'`; // bcz: hack ... want to quote the string the right way. if there are nested "'s, then use ' instead of ". In this case, test for the start of a JSON string of the format {"property": ... } and use outer 's instead of "s + return `"${field}"`; + case 'number': + case 'boolean': + return String(field); } - if (typeof field === 'number' || typeof field === 'boolean') return String(field); - if (field === undefined || field === null) return 'null'; - return field[ToScriptString](); + return field?.[ToScriptString]?.() ?? 'null'; } export function toString(field: Field): string { - if (typeof field === 'string') return field; - if (typeof field === 'number' || typeof field === 'boolean') return String(field); - if (field instanceof ObjectField) return field[ToString](); - if (field instanceof RefField) return field[ToString](); - return ''; + if (typeof field === 'string' || typeof field === 'number' || typeof field === 'boolean') return String(field); + return field?.[ToString]?.() || ''; } export function IsField(field: any): field is Field; export function IsField(field: any, includeUndefined: true): field is Field | undefined; @@ -79,17 +78,14 @@ export async function DocCastAsync(field: FieldResult): Promise> { return Cast(field, Doc); } -export function NumListCast(field: FieldResult) { - return Cast(field, listSpec('number'), []); +export function NumListCast(field: FieldResult, defaultVal: number[] = []) { + return Cast(field, listSpec('number'), defaultVal); } -export function StrListCast(field: FieldResult) { - return Cast(field, listSpec('string'), []); +export function StrListCast(field: FieldResult, defaultVal: string[] = []) { + return Cast(field, listSpec('string'), defaultVal); } -export function DocListCast(field: FieldResult) { - return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[]; -} -export function DocListCastOrNull(field: FieldResult) { - return Cast(field, listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[] | undefined; +export function DocListCast(field: FieldResult, defaultVal: Doc[] = []) { + return Cast(field, listSpec(Doc), defaultVal).filter(d => d instanceof Doc) as Doc[]; } export const WidthSym = Symbol('Width'); @@ -153,17 +149,8 @@ export function updateCachedAcls(doc: Doc) { } @scriptingGlobal -@Deserializable('Doc', updateCachedAcls).withFields(['id']) +@Deserializable('Doc', updateCachedAcls, ['id']) export class Doc extends RefField { - //TODO tfs: these should be temporary... - private static mainDocId: string | undefined; - public static get MainDocId() { - return this.mainDocId; - } - public static set MainDocId(id: string | undefined) { - this.mainDocId = id; - } - @observable public static CurrentlyLoading: Doc[]; // removes from currently loading display @action @@ -582,21 +569,13 @@ export namespace Doc { // compare whether documents or their protos match export function AreProtosEqual(doc?: Doc, other?: Doc) { - if (!doc || !other) return false; - const r = doc === other; - const r2 = Doc.GetProto(doc) === other; - const r3 = Doc.GetProto(other) === doc; - const r4 = Doc.GetProto(doc) === Doc.GetProto(other) && Doc.GetProto(other) !== undefined; - return r || r2 || r3 || r4; + return doc && other && Doc.GetProto(doc) === Doc.GetProto(other); } // Gets the data document for the document. Note: this is mis-named -- it does not specifically // return the doc's proto, but rather recursively searches through the proto inheritance chain // and returns the document who's proto is undefined or whose proto is marked as a base prototype ('isPrototype'). export function GetProto(doc: Doc): Doc { - if (doc instanceof Promise) { - // console.log("GetProto: warning: got Promise insead of Doc"); - } const proto = doc && (Doc.GetT(doc, 'isPrototype', 'boolean', true) ? doc : doc.proto || doc); return proto === doc ? proto : Doc.GetProto(proto); } diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index b5eca78dd..feb419597 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -103,30 +103,9 @@ export class ScriptField extends ObjectField { } this.rawscript = rawscript; this.setterscript = setterscript; - this.script = script ?? (CompileScript('false', { addReturn: true }) as CompiledScript); + this.script = script ?? ScriptField.GetScriptFieldCache('false:') ?? (CompileScript('false', { addReturn: true }) as CompiledScript); } - // init(callback: (res: Field) => any) { - // const options = this.options!; - // const keys = Object.keys(options.options.capturedIds); - // Server.GetFields(keys).then(fields => { - // let captured: { [name: string]: Field } = {}; - // keys.forEach(key => captured[options.options.capturedIds[key]] = fields[key]); - // const opts: ScriptOptions = { - // addReturn: options.options.addReturn, - // params: options.options.params, - // requiredType: options.options.requiredType, - // capturedVariables: captured - // }; - // const script = CompileScript(options.script, opts); - // if (!script.compiled) { - // throw new Error("Can't compile script"); - // } - // this._script = script; - // callback(this); - // }); - // } - [Copy](): ObjectField { return new ScriptField(this.script, this.setterscript, this.rawscript); } @@ -172,7 +151,7 @@ export class ComputedField extends ScriptField { _lastComputedResult: any; //TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc value = computedFn((doc: Doc) => this._valueOutsideReaction(doc)); - _valueOutsideReaction = (doc: Doc) => (this._lastComputedResult = this.script.run({ this: doc, self: Cast(doc.rootDocument, Doc, null) || doc, _last_: this._lastComputedResult, _readOnly_: true }, console.log).result); + _valueOutsideReaction = (doc: Doc) => (this._lastComputedResult = this.script.run({ this: doc, self: Cast(doc.rootDocument, Doc, null) ?? doc, value: '', _last_: this._lastComputedResult, _readOnly_: true }, console.log).result); [ToValue](doc: Doc) { return ComputedField.toValue(doc, this); @@ -181,12 +160,8 @@ export class ComputedField extends ScriptField { return new ComputedField(this.script, this.setterscript, this.rawscript); } - public static MakeScript(script: string, params: object = {}) { - const compiled = ScriptField.CompileScript(script, params, false); - return compiled.compiled ? new ComputedField(compiled) : undefined; - } public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }, setterscript?: string) { - const compiled = ScriptField.CompileScript(script, params, true, capturedVariables); + const compiled = ScriptField.CompileScript(script, params, true, { value: '', ...capturedVariables }); const compiledsetter = setterscript ? ScriptField.CompileScript(setterscript, { ...params, value: 'any' }, false, capturedVariables) : undefined; const compiledsetscript = compiledsetter?.compiled ? compiledsetter : undefined; return compiled.compiled ? new ComputedField(compiled, compiledsetscript) : undefined; diff --git a/src/fields/util.ts b/src/fields/util.ts index 6ff13d5d3..70d9ed61f 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -48,15 +48,11 @@ export function TraceMobx() { } const _setterImpl = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean { - if (SerializationHelper.IsSerializing()) { + if (SerializationHelper.IsSerializing() || typeof prop === 'symbol') { target[prop] = value; return true; } - if (typeof prop === 'symbol') { - target[prop] = value; - return true; - } if (value !== undefined) { value = value[SelfProxy] || value; } @@ -95,12 +91,8 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number delete target.__fields[prop]; } else { target.__fieldKeys && (target.__fieldKeys[prop] = true); - // if (target.__fields[prop] !== value) { - // console.log("ASSIGN " + prop + " " + value); - // } target.__fields[prop] = value; } - //if (typeof value === "object" && !(value instanceof ObjectField)) debugger; if (writeToServer) { if (value === undefined) target[Update]({ $unset: { ['fields.' + prop]: '' } }); @@ -143,13 +135,6 @@ export function denormalizeEmail(email: string) { return email.replace(/__/g, '.'); } -// playground mode allows the user to add/delete documents or make layout changes without them saving to the server -// let playgroundMode = false; - -// export function togglePlaygroundMode() { -// playgroundMode = !playgroundMode; -// } - /** * Copies parent's acl fields to the child */ @@ -317,7 +302,6 @@ export function setter(target: any, in_prop: string | symbol | number, value: an if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin && !(effectiveAcl === AclSelfEdit && value instanceof RichTextField)) return true; // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't if (typeof prop === 'string' && prop.startsWith('acl') && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value))) return true; - // if (typeof prop === "string" && prop.startsWith("acl") && !["Can Edit", "Can Augment", "Can View", "Not Shared", undefined].includes(value)) return true; if (typeof prop === 'string' && prop !== '__id' && prop !== '__fields' && prop.startsWith('_')) { if (!prop.startsWith('__')) prop = prop.substring(1); @@ -377,9 +361,9 @@ export function getField(target: any, prop: string | number, ignoreProto: boolea export function deleteProperty(target: any, prop: string | number | symbol) { if (typeof prop === 'symbol') { delete target[prop]; - return true; + } else { + target[SelfProxy][prop] = undefined; } - target[SelfProxy][prop] = undefined; return true; } -- cgit v1.2.3-70-g09d2 From 0d899471be676f6619244350982630b151bb4b41 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 28 Mar 2023 19:53:49 -0400 Subject: mostly code cleaning. small bugs --- src/client/documents/Documents.ts | 4 +- src/client/util/CurrentUserUtils.ts | 2 +- src/client/views/OverlayView.tsx | 6 +- src/client/views/StyleProvider.tsx | 12 +-- .../views/collections/CollectionTimeView.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 1 + .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/DocumentView.scss | 6 +- src/client/views/nodes/DocumentView.tsx | 93 ++++++++++------------ src/client/views/nodes/LinkAnchorBox.tsx | 32 +++----- src/client/views/nodes/trails/PresBox.tsx | 5 +- 11 files changed, 76 insertions(+), 89 deletions(-) (limited to 'src/client/views/OverlayView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index c484db0db..a110782b2 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -263,6 +263,7 @@ export class DocumentOptions { defaultBackgroundColor?: string; _isLinkButton?: boolean; // marks a document as a button that will follow its primary link when clicked _linkAutoMove?: boolean; // whether link endpoint should move around the edges of a document to make shortest path to other link endpoint + 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 @@ -505,6 +506,7 @@ export namespace Docs { options: { childDontRegisterViews: true, _isLinkButton: true, + hideLinkAnchors: true, _height: 150, description: '', showCaption: 'description', @@ -581,7 +583,7 @@ export namespace Docs { DocumentType.PRES, { layout: { view: PresBox, dataField: defaultDataKey }, - options: { defaultDoubleClick: 'ignore' }, + options: { defaultDoubleClick: 'ignore', hideLinkAnchors: true }, }, ], [ diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b21f53221..753cd1cb7 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -301,7 +301,7 @@ export class CurrentUserUtils { { toolTip: "Tap or drag to create a data viz node", title: "DataViz", icon: "file", dragFactory: doc.emptyDataViz as Doc, clickFactory: DocCast(doc.emptyDataViz)}, { toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "file", dragFactory: doc.emptySlide as Doc, clickFactory: DocCast(doc.emptySlide), openFactoryLocation: OpenWhere.overlay}, { toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize", dragFactory: doc.emptyHeader as Doc, clickFactory: DocCast(doc.emptyHeader), openFactoryAsDelegate: true }, - { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", clickFactory: "repl" as any, openFactoryLocation: OpenWhere.overlay}, + { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", clickFactory: 'repl' as any, openFactoryLocation: OpenWhere.overlay}, ].map(tuple => ( { openFactoryLocation: OpenWhere.addRight, scripts: { onClick: 'openDoc(copyDragFactory(this.clickFactory,this.openFactoryAsDelegate), this.openFactoryLocation)', diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 34e8cd6dd..ec22128d4 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -219,6 +219,7 @@ export class OverlayView extends React.Component { PanelHeight={d[HeightSym]} ScreenToLocalTransform={this.docScreenToLocalXf(d)} renderDepth={1} + hideDecorations={true} isDocumentActive={returnTrue} isContentActive={returnTrue} whenChildContentsActiveChanged={emptyFunction} @@ -252,8 +253,3 @@ export class OverlayView extends React.Component { ); } } -// bcz: ugh ... want to be able to pass ScriptingRepl as tag argument, but that doesn't seem to work.. runtime error -ScriptingGlobals.add(function addOverlayWindow(type: string, options: OverlayElementOptions) { - OverlayView.Instance.addWindow(, options); - addOverlayWindow('ScriptingRepl', { x: 300, y: 100, width: 200, height: 200, title: 'Scripting REPL' }); -}); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index faaa4e1f9..bd0539069 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -36,10 +36,10 @@ export enum StyleProp { FillColor = 'fillColor', // fill color of an ink stroke or shape WidgetColor = 'widgetColor', // color to display UI widgets on a document view -- used for the sidebar divider dragger on a text note HideLinkButton = 'hideLinkButton', // hides the blue-dot link button. used when a document acts like a button - LinkSource = 'linkSource', // source document of a link -- used by LinkAnchorBox PointerEvents = 'pointerEvents', // pointer events for DocumentView -- inherits pointer events if not specified Decorations = 'decorations', // additional decoration to display above a DocumentView -- currently only used to display a Lock for making things background HeaderMargin = 'headerMargin', // margin at top of documentview, typically for displaying a title -- doc contents will start below that + ShowCaption = 'showCaption', TitleHeight = 'titleHeight', // Height of Title area ShowTitle = 'showTitle', // whether to display a title on a Document (optional :hover suffix) JitterRotation = 'jitterRotation', // whether documents should be randomly rotated @@ -182,6 +182,8 @@ export function DefaultStyleProvider(doc: Opt, props: Opt (props?.PanelHeight() || 0) ? 5 : 10) : 0; + case StyleProp.ShowCaption: + return doc?._viewType === CollectionViewType.Carousel || props?.hideCaptions ? undefined : StrCast(doc?._showCaption); case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.NoteTaking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._viewType as any) || (doc?.type === DocumentType.RTF && !showTitle()?.includes('noMargin')) || @@ -299,12 +301,12 @@ export function DefaultStyleProvider(doc: Opt, props: Opt 0 ? ( + if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform) { + return doc?.pointerEvents !== 'none' ? null : (
toggleLockedPosition(doc)}> - +
- ) : null; + ); } } } diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 89b2fbfe3..4d5978548 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -150,7 +150,7 @@ export class CollectionTimeView extends CollectionSubView() { engineProps={{ pivotField: this.pivotField, docFilters: this.childDocFilters, docRangeFilters: this.childDocRangeFilters }} fitContentsToBox={returnTrue} childClickScript={this._childClickedScript} - viewDefDivClick={this._viewDefDivClick} + viewDefDivClick={this.layoutEngine() === computeTimelineLayout.name ? undefined : this._viewDefDivClick} //dontScaleFilter={this.dontScaleFilter} layoutEngine={this.layoutEngine} /> diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 93b816581..4ad09628f 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -422,6 +422,7 @@ export class TabDocView extends React.Component { })} renderDepth={0} LayoutTemplateString={this.props.keyValue ? KeyValueBox.LayoutString() : undefined} + hideTitle={this.props.keyValue} Document={this._document} DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined} ContainingCollectionView={undefined} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 0714bffbc..f16371592 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -609,7 +609,7 @@ export class MarqueeView extends React.Component { const layoutDoc = Doc.Layout(doc); diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index e5913d997..1265651ad 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -17,8 +17,7 @@ top: 0; } -.documentView-node, -.documentView-node-topmost { +.documentView-node { position: inherit; top: 0; left: 0; @@ -209,8 +208,7 @@ } } -.documentView-node:hover, -.documentView-node-topmost:hover { +.documentView-node:hover { > .documentView-styleWrapper { > .documentView-titleWrapper-hover { display: inline-block; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9a3e77e6e..ab93ce87b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -268,9 +268,6 @@ export class DocumentViewInternal extends DocComponent; // milliseconds for animating between views. defaults to 300 if not uset @observable _animateScalingTo = 0; - private get topMost() { - return this.props.renderDepth === 0 && !LightboxView.LightboxDoc; - } public get animateScaleTime() { return this._animateScaleTime ?? 300; } @@ -307,8 +304,9 @@ export class DocumentViewInternal extends DocComponent { - this.props.ContainingCollectionView?.removeDocument(this.props.Document), RadialMenu.Instance.closeMenu(); + this.props.ContainingCollectionView?.removeDocument(this.props.Document); + RadialMenu.Instance.closeMenu(); }, icon: 'external-link-square-alt', selected: -1, @@ -563,13 +565,11 @@ export class DocumentViewInternal extends DocComponent { - if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) return; + if (e.buttons !== 1 || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) return; - if (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { - if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { - this.cleanupPointerEvents(); - this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'alias') || ((this.Document.dropAction || this.props.dropAction || undefined) as dropActionType)); - } + if (!Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, Date.now())) { + this.cleanupPointerEvents(); + this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'alias') || ((this.Document.dropAction || this.props.dropAction || undefined) as dropActionType)); } }; @@ -888,7 +888,7 @@ export class DocumentViewInternal extends DocComponent d.linkDisplay); return filtered.map((link, i) => (
@@ -1059,11 +1056,13 @@ export class DocumentViewInternal extends DocComponent this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1; const showTitle = this.ShowTitle?.split(':')[0]; const showTitleHover = this.ShowTitle?.includes(':hover'); - const showCaption = !this.props.hideCaptions && this.Document._viewType !== CollectionViewType.Carousel ? StrCast(this.layoutDoc._showCaption) : undefined; - const captionView = !showCaption ? null : ( + const captionView = !this.showCaption ? null : (
@@ -1161,12 +1159,13 @@ export class DocumentViewInternal extends DocComponent @@ -1220,7 +1219,7 @@ export class DocumentViewInternal extends DocComponent
); - return this.props.hideTitle || (!showTitle && !showCaption) ? ( + return this.props.hideTitle || (!showTitle && !this.showCaption) ? ( this.contents ) : (
@@ -1235,31 +1234,27 @@ export class DocumentViewInternal extends DocComponent { TraceMobx(); - const thumb = ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url?.href.replace('.png', '_m.png'); - const background = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor + ':box'); - if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || this.hidden) return null; - return ( - this.docContents ?? ( -
- {this.innards} - {!this.disableClickScriptFunc && this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ?
: null} - {this.widgetDecorations ?? null} -
- ) - ); + return !DocCast(this.Document) || GetEffectiveAcl(this.Document[DataSym]) === AclPrivate || this.hidden + ? null + : this.docContents ?? ( +
+ {this.innards} + {this.widgetDecorations ?? null} +
+ ); }; /** @@ -1506,7 +1501,7 @@ export class DocumentView extends React.Component { return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth(); } @computed get panelHeight() { - if (this.effectiveNativeHeight && !this.layoutDoc.nativeHeightUnfrozen) { + if (this.effectiveNativeHeight && (!this.fitWidth || !this.layoutDoc.nativeHeightUnfrozen)) { return Math.min(this.props.PanelHeight(), this.effectiveNativeHeight * this.nativeScaling); } return this.props.PanelHeight(); @@ -1528,7 +1523,7 @@ export class DocumentView extends React.Component { toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight()); getBounds = () => { - if (!this.docView || !this.docView.ContentDiv || this.props.Document.type === DocumentType.PRES || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) { + if (!this.docView || !this.docView.ContentDiv || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) { return undefined; } const xf = this.docView?.props diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index e12548f18..3feb95ce9 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -1,22 +1,19 @@ -import { action, observable } from 'mobx'; +import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Doc } from '../../../fields/Doc'; -import { Cast, NumCast, StrCast } from '../../../fields/Types'; +import { NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { LinkFollower } from '../../util/LinkFollower'; -import { ContextMenu } from '../ContextMenu'; -import { ContextMenuProps } from '../ContextMenuItem'; +import { SelectionManager } from '../../util/SelectionManager'; import { ViewBoxBaseComponent } from '../DocComponent'; import { StyleProp } from '../StyleProvider'; import { FieldView, FieldViewProps } from './FieldView'; import './LinkAnchorBox.scss'; import { LinkDocPreview } from './LinkDocPreview'; import React = require('react'); -import { LinkManager } from '../../util/LinkManager'; import globalCssVariables = require('../global/globalCssVariables.scss'); -import { SelectionManager } from '../../util/SelectionManager'; @observer export class LinkAnchorBox extends ViewBoxBaseComponent() { @@ -31,19 +28,15 @@ export class LinkAnchorBox extends ViewBoxBaseComponent() { @observable _x = 0; @observable _y = 0; + @computed get linkSource() { + return this.props.docViewPath()[this.props.docViewPath().length - 2].rootDoc; // this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.LinkSource); + } + onPointerDown = (e: React.PointerEvent) => { - const anchorContainerDoc = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.LinkSource); - setupMoveUpEvents( - this, - e, - this.onPointerMove, - emptyFunction, - (e, doubleTap) => { - if (doubleTap) LinkFollower.FollowLink(this.rootDoc, anchorContainerDoc, false); - else this.props.select(false); - }, - false - ); + setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, (e, doubleTap) => { + if (doubleTap) LinkFollower.FollowLink(this.rootDoc, this.linkSource, false); + else this.props.select(false); + }); }; onPointerMove = action((e: PointerEvent, down: number[], delta: number[]) => { const cdiv = this._ref?.current?.parentElement; @@ -73,7 +66,6 @@ export class LinkAnchorBox extends ViewBoxBaseComponent() { const small = this.props.PanelWidth() <= 1; // this happens when rendered in a treeView const x = NumCast(this.rootDoc[this.fieldKey + '_x'], 100); const y = NumCast(this.rootDoc[this.fieldKey + '_y'], 100); - const linkSource = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.LinkSource); const background = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.BackgroundColor + ':anchor'); const anchor = this.fieldKey === 'anchor1' ? 'anchor2' : 'anchor1'; const anchorScale = !this.dataDoc[this.fieldKey + '-useLinkSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25; @@ -87,7 +79,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent() { onPointerEnter={e => LinkDocPreview.SetLinkInfo({ docProps: this.props, - linkSrc: linkSource, + linkSrc: this.linkSource, linkDoc: this.rootDoc, showHeader: true, location: [e.clientX, e.clientY + 20], diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 3376c29a9..0afb36214 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -919,6 +919,7 @@ export class PresBox extends ViewBoxBaseComponent() { */ @action enterMinimize = () => { + this.updateCurrentPresentation(this.rootDoc); clearTimeout(this._presTimer); const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); this.props.removeDocument?.(this.layoutDoc); @@ -939,7 +940,7 @@ export class PresBox extends ViewBoxBaseComponent() { doc._height = 30; doc._width = PresBox.minimizedWidth; Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc); - PresBox.Instance.initializePresState(PresBox.Instance.itemIndex); + PresBox.Instance?.initializePresState(PresBox.Instance.itemIndex); return (doc.presStatus = PresStatus.Manual); } @@ -2435,7 +2436,7 @@ export class PresBox extends ViewBoxBaseComponent() {
Slide {this.itemIndex + 1} - {this.activeItem.presIndexed !== undefined ? `(${this.activeItem.presIndexed}/${this.progressivizedItems(this.activeItem)?.length})` : ''} / {this.childDocs.length} + {this.activeItem?.presIndexed !== undefined ? `(${this.activeItem.presIndexed}/${this.progressivizedItems(this.activeItem)?.length})` : ''} / {this.childDocs.length}
setupMoveUpEvents(this, e, returnFalse, returnFalse, this.exitClicked, false, false)}> -- cgit v1.2.3-70-g09d2 From 5cd64622f14ede408d3baca4a10d155b60392e46 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 13 Apr 2023 22:22:07 -0400 Subject: lots of changes to get rid of ContainingCollectionDoc and ContainingCollectionView props. --- src/client/documents/Documents.ts | 8 +-- src/client/util/CurrentUserUtils.ts | 2 +- src/client/util/DocumentManager.ts | 83 ++++++++++------------ src/client/util/DragManager.ts | 4 +- .../util/Import & Export/DirectoryImportBox.tsx | 6 +- src/client/util/ReplayMovements.ts | 64 ++++++----------- src/client/util/SelectionManager.ts | 6 +- src/client/util/SharingManager.tsx | 5 +- src/client/util/TrackMovements.ts | 36 +++++----- src/client/views/DashboardView.tsx | 17 ++--- src/client/views/DocComponent.tsx | 1 - src/client/views/DocumentDecorations.tsx | 8 +-- src/client/views/GestureOverlay.tsx | 4 +- src/client/views/InkStrokeProperties.ts | 3 +- src/client/views/LightboxView.tsx | 4 +- src/client/views/MainView.tsx | 20 ++---- src/client/views/OverlayView.tsx | 10 +-- src/client/views/Palette.tsx | 31 ++++---- src/client/views/PropertiesDocContextSelector.tsx | 22 ++++-- src/client/views/PropertiesView.tsx | 2 - src/client/views/StyleProvider.tsx | 11 ++- src/client/views/TemplateMenu.tsx | 2 - .../views/collections/CollectionCarouselView.tsx | 1 - src/client/views/collections/CollectionMenu.tsx | 6 +- .../views/collections/CollectionNoteTakingView.tsx | 4 -- .../views/collections/CollectionPileView.tsx | 2 +- .../collections/CollectionStackedTimeline.tsx | 2 - .../views/collections/CollectionStackingView.tsx | 4 -- .../views/collections/CollectionTreeView.tsx | 10 +-- src/client/views/collections/CollectionView.tsx | 5 +- src/client/views/collections/TabDocView.tsx | 6 +- src/client/views/collections/TreeView.tsx | 54 ++++++-------- .../collectionFreeForm/CollectionFreeFormView.tsx | 31 ++++---- .../collectionLinear/CollectionLinearView.tsx | 5 +- .../CollectionMulticolumnView.tsx | 2 - .../CollectionMultirowView.tsx | 2 - .../collectionSchema/CollectionSchemaView.tsx | 6 +- .../collections/collectionSchema/SchemaRowBox.tsx | 2 +- .../collectionSchema/SchemaTableCell.tsx | 2 - src/client/views/linking/LinkMenuItem.tsx | 21 +----- src/client/views/linking/LinkPopup.tsx | 4 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 7 +- src/client/views/nodes/DocumentContentsView.tsx | 1 - src/client/views/nodes/DocumentLinksButton.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 12 ++-- src/client/views/nodes/FieldView.tsx | 1 + src/client/views/nodes/KeyValuePair.tsx | 2 - src/client/views/nodes/LinkDocPreview.tsx | 4 +- src/client/views/nodes/ScreenshotBox.tsx | 5 +- src/client/views/nodes/button/FontIconBox.tsx | 4 +- .../views/nodes/formattedText/DashDocView.tsx | 7 +- .../views/nodes/formattedText/DashFieldView.tsx | 5 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 20 ++++-- src/client/views/nodes/trails/PresBox.tsx | 1 - src/client/views/nodes/trails/PresElementBox.tsx | 27 ++++--- src/client/views/topbar/TopBar.tsx | 51 ++----------- src/mobile/AudioUpload.tsx | 76 ++++++++++---------- src/mobile/MobileInterface.tsx | 4 +- 58 files changed, 291 insertions(+), 456 deletions(-) (limited to 'src/client/views/OverlayView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 8d97ce5af..09b45a481 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1285,10 +1285,6 @@ export namespace DocUtils { return rangeFilteredDocs; } - export function DefaultFocus(doc: Doc, options: DocFocusOptions) { - return undefined; - } - export let ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) { @@ -1299,10 +1295,9 @@ export namespace DocUtils { }); } - export function MakeLink(source: Doc, target: Doc, linkSettings: { linkRelationship?: string; description?: string; linkDisplay?: boolean }, id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) { + export function MakeLink(source: Doc, target: Doc, linkSettings: { linkRelationship?: string; description?: string; linkDisplay?: boolean }, id?: string, showPopup?: number[]) { if (!linkSettings.linkRelationship) linkSettings.linkRelationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; const sv = DocumentManager.Instance.getDocumentView(source); - if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target) return; if (target.doc === Doc.UserDoc()) return undefined; const makeLink = action((linkDoc: Doc, showPopup?: number[]) => { @@ -1371,7 +1366,6 @@ export namespace DocUtils { value: 'any', _readOnly_: 'boolean', scriptContext: 'any', - thisContainer: Doc.name, documentView: Doc.name, heading: Doc.name, checked: 'boolean', diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 76ea3e3ea..abf7313a4 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -110,7 +110,7 @@ export class CurrentUserUtils { const tempClicks = DocCast(doc[field]); const reqdClickOpts:DocumentOptions = {_width: 300, _height:200, system: true}; const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [ - { opts: { title: "Open In Target", targetScriptKey: "onChildClick"}, script: "docCast(thisContainer.target).then((target) => target && (target.proto.data = new List([self])))"}, + { opts: { title: "Open In Target", targetScriptKey: "onChildClick"}, script: "docCast(documentView?.props.docViewPath().lastElement()?.rootDoc.target).then((target) => target && (target.proto.data = new List([self])))"}, { opts: { title: "Open Detail On Right", targetScriptKey: "onChildDoubleClick"}, script: `openDoc(self.doubleClickView.${OpenWhere.addRight})`}]; const reqdClickList = reqdTempOpts.map(opts => { const allOpts = {...reqdClickOpts, ...opts.opts}; diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index ccf370662..ad89e8653 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,15 +1,11 @@ -import { loadAsync } from 'jszip'; -import { action, observable, ObservableSet, runInAction } from 'mobx'; +import { action, observable, ObservableSet } from 'mobx'; import { AnimationSym, Doc, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { listSpec } from '../../fields/Schema'; import { Cast, DocCast, StrCast } from '../../fields/Types'; import { AudioField } from '../../fields/URLField'; -import { emptyFunction } from '../../Utils'; import { CollectionViewType } from '../documents/DocumentTypes'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; -import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; -import { CollectionView } from '../views/collections/CollectionView'; import { TabDocView } from '../views/collections/TabDocView'; import { LightboxView } from '../views/LightboxView'; import { MainView } from '../views/MainView'; @@ -39,7 +35,7 @@ export class DocumentManager { private _viewRenderedCbs: { doc: Doc; func: (dv: DocumentView) => any }[] = []; public AddViewRenderedCb = (doc: Opt, func: (dv: DocumentView) => any) => { if (doc) { - const dv = this.getDocumentViewById(doc[Id]); + const dv = this.getDocumentView(doc); this._viewRenderedCbs.push({ doc, func }); if (dv) { this.callAddViewFuncs(dv); @@ -129,42 +125,24 @@ export class DocumentManager { return this.getDocumentViewsById(doc[Id]); } - public getDocumentViewById(id: string, preferredCollection?: CollectionView): DocumentView | undefined { - if (!id) return undefined; - let toReturn: DocumentView | undefined; - const passes = preferredCollection ? [preferredCollection, undefined] : [undefined]; - - for (const pass of passes) { - Array.from(DocumentManager.Instance.DocumentViews).map(view => { - if (view.rootDoc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) { - toReturn = view; - return; - } - }); - if (!toReturn) { - Array.from(DocumentManager.Instance.DocumentViews).map(view => { - const doc = view.rootDoc.proto; - if (doc && doc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) { - toReturn = view; - } - }); - } else { - break; - } - } - - return toReturn; - } - - public getDocumentView(toFind: Doc, preferredCollection?: CollectionView): DocumentView | undefined { - const found = + public getDocumentView(toFind: Doc | undefined, preferredCollection?: DocumentView): DocumentView | undefined { + const doc = + // bcz: this was temporary code used to match documents by data url instead of by id. intended only for repairing the DB // Array.from(DocumentManager.Instance.DocumentViews).find( // dv => // ((dv.rootDoc.data as any)?.url?.href && (dv.rootDoc.data as any)?.url?.href === (toFind.data as any)?.url?.href) || // ((DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href && (DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href === (DocCast(toFind.annotationOn)?.data as any)?.url?.href) // )?.rootDoc ?? toFind; - return this.getDocumentViewById(found[Id], preferredCollection); + const docViewArray = Array.from(DocumentManager.Instance.DocumentViews); + const passes = !doc ? [] : preferredCollection ? [preferredCollection, undefined] : [undefined]; + return passes.reduce( + (pass, toReturn) => + toReturn ?? + docViewArray.filter(view => view.rootDoc === doc).find(view => !pass || view.props.docViewPath().lastElement() === preferredCollection) ?? + docViewArray.filter(view => Doc.GetProto(view.rootDoc) === doc).find(view => !pass || view.props.docViewPath().lastElement() === preferredCollection), + undefined as Opt + ); } public getLightboxDocumentView = (toFind: Doc, originatingDoc: Opt = undefined): DocumentView | undefined => { @@ -247,7 +225,8 @@ export class DocumentManager { public showDocumentView = async (targetDocView: DocumentView, options: DocFocusOptions) => { const docViewPath = targetDocView.docViewPath.slice(); let rootContextView = docViewPath.shift(); - return rootContextView && this.focusViewsInPath(rootContextView, options, async () => ({ childDocView: docViewPath.shift(), viewSpec: undefined })); + await (rootContextView && this.focusViewsInPath(rootContextView, options, async () => ({ childDocView: docViewPath.shift(), viewSpec: undefined }))); + if (options.toggleTarget && (!options.didMove || targetDocView.rootDoc.hidden)) targetDocView.rootDoc.hidden = !targetDocView.rootDoc.hidden; }; // shows a document by first: @@ -318,15 +297,25 @@ export class DocumentManager { } } } -export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) { - const cv = collectionDoc && DocumentManager.Instance.getDocumentView(collectionDoc); - const dv = DocumentManager.Instance.getDocumentView(doc, (cv?.ComponentView as CollectionFreeFormView)?.props.CollectionView); - if (dv) { - DocumentManager.Instance.showDocumentView(dv, { willZoomCentered: true }); - } else { - const context = doc.context !== Doc.MyFilesystem && Cast(doc.context, Doc, null); - const showDoc = context || doc; - DocumentManager.Instance.showDocument(Doc.BestAlias(showDoc), { openLocation: OpenWhere.addRight }, () => DocumentManager.Instance.showDocument(doc, { willZoomCentered: true })); - } +export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCentered: true, openLocation: OpenWhere.addRight }, containingDoc?: Doc) { + const func = () => { + const cv = DocumentManager.Instance.getDocumentView(containingDoc); + const dv = DocumentManager.Instance.getDocumentView(doc, cv); + if (dv && (!containingDoc || dv.props.docViewPath().lastElement()?.Document === containingDoc)) { + DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.rootDoc)); + } else { + const showDoc = Doc.BestAlias(DocCast((containingDoc ?? doc.context) !== Doc.MyFilesystem ? containingDoc ?? doc.context : undefined, doc)); + DocumentManager.Instance.showDocument(showDoc, { ...options, toggleTarget: undefined }, () => DocumentManager.Instance.showDocument(doc, options)).then(() => { + const cv = DocumentManager.Instance.getDocumentView(containingDoc); + const dv = DocumentManager.Instance.getDocumentView(doc, cv); + dv && Doc.linkFollowHighlight(dv.rootDoc); + }); + } + }; + if (doc.hidden) { + doc.hidden = false; + options.toggleTarget = false; + setTimeout(func); + } else func(); } ScriptingGlobals.add(DocFocusOrOpen); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 7d2aa813f..7e6de5e67 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -3,10 +3,8 @@ import { DateField } from '../../fields/DateField'; import { Doc, Field, Opt, StrListCast } from '../../fields/Doc'; import { List } from '../../fields/List'; import { PrefetchProxy } from '../../fields/Proxy'; -import { listSpec } from '../../fields/Schema'; -import { SchemaHeaderField } from '../../fields/SchemaHeaderField'; import { ScriptField } from '../../fields/ScriptField'; -import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../fields/Types'; +import { BoolCast, ScriptCast, StrCast } from '../../fields/Types'; import { emptyFunction, Utils } from '../../Utils'; import { Docs, DocUtils } from '../documents/Documents'; import * as globalCssVariables from '../views/global/globalCssVariables.scss'; diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index 559958c2b..76b1323c4 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -14,12 +14,14 @@ import { AcceptableMedia, Upload } from '../../../server/SharedMediaTypes'; import { Utils } from '../../../Utils'; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents'; +import { DocumentType } from '../../documents/DocumentTypes'; import { Networking } from '../../Network'; import { FieldView, FieldViewProps } from '../../views/nodes/FieldView'; import { DocumentManager } from '../DocumentManager'; import './DirectoryImportBox.scss'; import ImportMetadataEntry, { keyPlaceholder, valuePlaceholder } from './ImportMetadataEntry'; import React = require('react'); +import _ = require('lodash'); const unsupported = ['text/html', 'text/plain']; @@ -155,8 +157,8 @@ export class DirectoryImportBox extends React.Component { x: NumCast(doc.x), y: NumCast(doc.y) + offset, }; - const parent = this.props.ContainingCollectionView; - if (parent) { + const parent = _.nth(this.props.docViewPath(), -2); // last element of path is this box's document view, 2nd to last is any collection or other document that may contain it. + if (parent?.rootDoc.type === DocumentType.COL) { let importContainer: Doc; if (docs.length < 50) { importContainer = Docs.Create.MasonryDocument(docs, options); diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts index d5bffc5e2..40261985a 100644 --- a/src/client/util/ReplayMovements.ts +++ b/src/client/util/ReplayMovements.ts @@ -7,6 +7,7 @@ import { CollectionDockingView } from '../views/collections/CollectionDockingVie import { DocServer } from '../DocServer'; import { Movement, Presentation } from './TrackMovements'; import { OpenWhereMod } from '../views/nodes/DocumentView'; +import { returnTransparent } from '../../Utils'; export class ReplayMovements { private timers: NodeJS.Timeout[] | null; @@ -60,17 +61,11 @@ export class ReplayMovements { return; } - let docIdtoDoc: Map = new Map(); - try { - docIdtoDoc = await this.loadPresentation(presentation); - } catch { - console.error('[recordingApi.ts] setVideoBox(): error loading presentation - no replay movements'); - throw 'error loading docs from server'; - } + const docIdtoDoc = this.loadPresentation(presentation); this.videoBoxDisposeFunc = reaction( () => ({ playing: videoBox._playing, timeViewed: videoBox.player?.currentTime || 0 }), - ({ playing, timeViewed }) => (playing ? this.playMovements(presentation, docIdtoDoc, timeViewed) : this.pauseMovements()) + ({ playing, timeViewed }) => (playing ? this.playMovements(presentation, timeViewed) : this.pauseMovements()) ); this.videoBox = videoBox; }; @@ -93,45 +88,33 @@ export class ReplayMovements { this.pauseMovements(); }; - loadPresentation = async (presentation: Presentation) => { + loadPresentation = (presentation: Presentation) => { const { movements } = presentation; if (movements === null) { throw '[recordingApi.ts] followMovements() failed: no presentation data'; } // generate a set of all unique docIds - const docIds = new Set(); - for (const { docId } of movements) { - if (!docIds.has(docId)) docIds.add(docId); - } - - const docIdtoDoc = new Map(); - - let refFields = await DocServer.GetRefFields([...docIds.keys()]); - for (const docId in refFields) { - if (!refFields[docId]) { - throw `one field was undefined`; - } - docIdtoDoc.set(docId, refFields[docId] as Doc); + const docs = new Set(); + for (const { doc } of movements) { + if (!docs.has(doc)) docs.add(doc); } - // console.info('loadPresentation refFields', refFields, docIdtoDoc); - return docIdtoDoc; + return docs; }; // returns undefined if the docView isn't open on the screen - getCollectionFFView = (docId: string) => { - const isInView = DocumentManager.Instance.getDocumentViewById(docId); + getCollectionFFView = (doc: Doc) => { + const isInView = DocumentManager.Instance.getDocumentView(doc); if (isInView) { return isInView.ComponentView as CollectionFreeFormView; } }; // will open the doc in a tab then return the CollectionFFView that holds it - openTab = (docId: string, docIdtoDoc: Map) => { - const doc = docIdtoDoc.get(docId); - if (doc == undefined) { - console.error(`docIdtoDoc did not contain docId ${docId}`); + openTab = (doc: Doc) => { + if (doc === undefined) { + console.error(`doc undefined`); return undefined; } // console.log('openTab', docId, doc); @@ -149,13 +132,12 @@ export class ReplayMovements { document.Document._panY = panY; }; - getFirstMovements = (movements: Movement[]): Map => { + getFirstMovements = (movements: Movement[]): Map => { if (movements === null) return new Map(); // generate a set of all unique docIds - const docIdtoFirstMove = new Map(); + const docIdtoFirstMove = new Map(); for (const move of movements) { - const { docId } = move; - if (!docIdtoFirstMove.has(docId)) docIdtoFirstMove.set(docId, move); + if (!docIdtoFirstMove.has(move.doc)) docIdtoFirstMove.set(move.doc, move); } return docIdtoFirstMove; }; @@ -165,7 +147,7 @@ export class ReplayMovements { Doc.UserDoc().presentationMode = 'none'; }; - public playMovements = (presentation: Presentation, docIdtoDoc: Map, timeViewed: number = 0) => { + public playMovements = (presentation: Presentation, timeViewed: number = 0) => { // console.info('playMovements', presentation, timeViewed, docIdtoDoc); if (presentation.movements === null || presentation.movements.length === 0) { @@ -183,13 +165,13 @@ export class ReplayMovements { const handleFirstMovements = () => { // if the first movement is a closed tab, open it const firstMovement = filteredMovements[0]; - const isClosed = this.getCollectionFFView(firstMovement.docId) === undefined; - if (isClosed) this.openTab(firstMovement.docId, docIdtoDoc); + const isClosed = this.getCollectionFFView(firstMovement.doc) === undefined; + if (isClosed) this.openTab(firstMovement.doc); // for the open tabs, set it to the first move const docIdtoFirstMove = this.getFirstMovements(filteredMovements); - for (const [docId, firstMove] of docIdtoFirstMove) { - const colFFView = this.getCollectionFFView(docId); + for (const [doc, firstMove] of docIdtoFirstMove) { + const colFFView = this.getCollectionFFView(doc); if (colFFView) this.zoomAndPan(firstMove, colFFView); } }; @@ -200,12 +182,12 @@ export class ReplayMovements { const timeDiff = movement.time - timeViewed * 1000; return setTimeout(() => { - const collectionFFView = this.getCollectionFFView(movement.docId); + const collectionFFView = this.getCollectionFFView(movement.doc); if (collectionFFView) { this.zoomAndPan(movement, collectionFFView); } else { // tab wasn't open - open it and play the movement - const openedColFFView = this.openTab(movement.docId, docIdtoDoc); + const openedColFFView = this.openTab(movement.doc); console.log('openedColFFView', openedColFFView); openedColFFView && this.zoomAndPan(movement, openedColFFView); } diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 313c255a0..bfad93334 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,4 +1,3 @@ -import { ModalManager } from '@material-ui/core'; import { action, observable, ObservableMap } from 'mobx'; import { computedFn } from 'mobx-utils'; import { Doc, Opt } from '../../fields/Doc'; @@ -64,8 +63,9 @@ export namespace SelectionManager { export function DeselectView(docView?: DocumentView): void { manager.DeselectView(docView); } - export function SelectView(docView: DocumentView, ctrlPressed: boolean): void { - manager.SelectView(docView, ctrlPressed); + export function SelectView(docView: DocumentView | undefined, ctrlPressed: boolean): void { + if (!docView) DeselectAll(); + else manager.SelectView(docView, ctrlPressed); } export function SelectSchemaViewDoc(document: Opt, deselectAllFirst?: boolean): void { if (deselectAllFirst) manager.DeselectAll(); diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 3c05af4bb..a73eda04c 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -370,11 +370,10 @@ export class SharingManager extends React.Component<{}> { const docs = SelectionManager.Views().length > 1 ? SelectionManager.Views().map(docView => docView.props.Document) : [this.targetDoc]; return ( { - let context: Opt; - if (this.targetDoc && this.targetDocView && docs.length === 1 && (context = this.targetDocView.props.ContainingCollectionView)) { + if (this.targetDoc && this.targetDocView && docs.length === 1) { DocumentManager.Instance.showDocument(this.targetDoc, { willZoomCentered: true }); } }} diff --git a/src/client/util/TrackMovements.ts b/src/client/util/TrackMovements.ts index 4a2ccd706..2f16307b3 100644 --- a/src/client/util/TrackMovements.ts +++ b/src/client/util/TrackMovements.ts @@ -9,7 +9,7 @@ export type Movement = { panX: number; panY: number; scale: number; - docId: string; + doc: Doc; }; export type Presentation = { @@ -28,7 +28,7 @@ export class TrackMovements { private tracking: boolean; private absoluteStart: number; // instance variable for holding the FFViews and their disposers - private recordingFFViews: Map | null; + private recordingFFViews: Map | null; private tabChangeDisposeFunc: IReactionDisposer | null; // create static instance and getter for global use @@ -55,33 +55,33 @@ export class TrackMovements { return this.currentPresentation.movements === null; } - private addRecordingFFView(doc: Doc, key: string = doc[Id]): void { + private addRecordingFFView(doc: Doc): void { // console.info('adding dispose func : docId', key, 'doc', doc); if (this.recordingFFViews === null) { console.warn('addFFView on null RecordingApi'); return; } - if (this.recordingFFViews.has(key)) { - console.warn('addFFView : key already in map'); + if (this.recordingFFViews.has(doc)) { + console.warn('addFFView : doc already in map'); return; } const disposeFunc = reaction( () => ({ x: NumCast(doc.panX, -1), y: NumCast(doc.panY, -1), scale: NumCast(doc.viewScale, 0) }), - res => res.x !== -1 && res.y !== -1 && this.tracking && this.trackMovement(res.x, res.y, key, res.scale) + res => res.x !== -1 && res.y !== -1 && this.tracking && this.trackMovement(res.x, res.y, doc, res.scale) ); - this.recordingFFViews?.set(key, disposeFunc); + this.recordingFFViews?.set(doc, disposeFunc); } - private removeRecordingFFView = (key: string) => { + private removeRecordingFFView = (doc: Doc) => { // console.info('removing dispose func : docId', key); if (this.recordingFFViews === null) { console.warn('removeFFView on null RecordingApi'); return; } - this.recordingFFViews.get(key)?.(); - this.recordingFFViews.delete(key); + this.recordingFFViews.get(doc)?.(); + this.recordingFFViews.delete(doc); }; // in the case where only one tab was changed (updates not across dashboards), set only one to true @@ -90,15 +90,15 @@ export class TrackMovements { // so that the size comparisons are correct, we must filter to only the FFViews const isFFView = (doc: Doc) => doc && 'viewType' in doc && doc.viewType === 'freeform'; - const tabbedFFViews = new Set(); + const tabbedFFViews = new Set(); for (const DashDoc of tabbedDocs) { - if (isFFView(DashDoc)) tabbedFFViews.add(DashDoc[Id]); + if (isFFView(DashDoc)) tabbedFFViews.add(DashDoc); } // new tab was added - need to add it if (tabbedFFViews.size > this.recordingFFViews.size) { for (const DashDoc of tabbedDocs) { - if (!this.recordingFFViews.has(DashDoc[Id])) { + if (!this.recordingFFViews.has(DashDoc)) { if (isFFView(DashDoc)) { this.addRecordingFFView(DashDoc); @@ -110,9 +110,9 @@ export class TrackMovements { } // tab was removed - need to remove it from recordingFFViews else if (tabbedFFViews.size < this.recordingFFViews.size) { - for (const [key] of this.recordingFFViews) { - if (!tabbedFFViews.has(key)) { - this.removeRecordingFFView(key); + for (const [doc] of this.recordingFFViews) { + if (!tabbedFFViews.has(doc)) { + this.removeRecordingFFView(doc); if (onlyOne) return; } } @@ -214,7 +214,7 @@ export class TrackMovements { } }; - private trackMovement = (panX: number, panY: number, docId: string, scale: number = 0) => { + private trackMovement = (panX: number, panY: number, doc: Doc, scale: number = 0) => { // ensure we are recording to track if (!this.tracking) { console.error('[recordingApi.ts] trackMovements(): tracking is false'); @@ -231,7 +231,7 @@ export class TrackMovements { // get the time const time = new Date().getTime() - this.absoluteStart; // make new movement object - const movement: Movement = { time, panX, panY, scale, docId }; + const movement: Movement = { time, panX, panY, scale, doc }; // add that movement to the current presentation data's movement array this.currentPresentation.movements && this.currentPresentation.movements.push(movement); diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 2b586b0e2..dee161931 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -109,17 +109,8 @@ export class DashboardView extends React.Component { />
-
); @@ -163,7 +154,7 @@ export class DashboardView extends React.Component {
-
this.selectDashboardGroup(DashboardGroup.MyDashboards)}> My Dashboards @@ -196,7 +187,7 @@ export class DashboardView extends React.Component { e.stopPropagation(); this.onContextMenu(dashboard, e); }}> - } /> +
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 9fc1487a0..a59189fd2 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -52,7 +52,6 @@ export function DocComponent

() { interface ViewBoxBaseProps { Document: Doc; DataDoc?: Doc; - ContainingCollectionDoc: Opt; DocumentView?: () => DocumentView; fieldKey: string; isSelected: (outsideReaction?: boolean) => boolean; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 9ffbe083f..9a4aaf00e 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -33,6 +33,7 @@ import { ImageBox } from './nodes/ImageBox'; import React = require('react'); import { RichTextField } from '../../fields/RichTextField'; import { LinkFollower } from '../util/LinkFollower'; +import _ = require('lodash'); @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> { @@ -303,8 +304,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P SelectionManager.DeselectAll(); }; - onSelectorClick = () => SelectionManager.Views()?.[0]?.props.ContainingCollectionView?.props.select(false); - + onSelectorClick = () => SelectionManager.Views()?.[0]?.props.docViewPath?.().lastElement()?.select(false); /** * Handles setting up events when user clicks on the border radius editor * @param e PointerEvent @@ -744,7 +744,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P seldocview.props.hideDeleteButton || seldocview.rootDoc.hideDeleteButton || SelectionManager.Views().some(docView => { - const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit; + const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DataSym]) : AclEdit; return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin); }); @@ -865,7 +865,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P

e.preventDefault()} />
e.preventDefault()} /> - {seldocview.props.renderDepth <= 1 || !seldocview.props.ContainingCollectionView ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectorClick, 'tap to select containing document')} + {seldocview.props.renderDepth <= 1 || !seldocview.props.docViewPath().lastElement() ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectorClick, 'tap to select containing document')} )} {useRounding && ( diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 0feccb742..13faae783 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -1010,14 +1010,12 @@ export class GestureOverlay extends Touchable { renderDepth={0} styleProvider={returnEmptyString} docViewPath={returnEmptyDoclist} - focus={DocUtils.DefaultFocus} + focus={emptyFunction} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} docRangeFilters={returnEmptyFilter} docFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} /> ); }; diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index e6df0801c..156825f41 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -373,10 +373,11 @@ export class InkStrokeProperties { snapToAllCurves = (screenDragPt: { X: number; Y: number }, inkView: DocumentView, snapData: { nearestPt: { X: number; Y: number }; distance: number }, ink: InkData, controlIndex: number) => { const containingCollection = inkView.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; + const containingDocView = inkView.props.CollectionFreeFormDocumentView?.().props.DocumentView?.(); containingCollection?.childDocs .filter(doc => doc.type === DocumentType.INK) .forEach(doc => { - const testInkView = DocumentManager.Instance.getDocumentView(doc, containingCollection?.props.CollectionView); + const testInkView = DocumentManager.Instance.getDocumentView(doc, containingDocView); const snapped = testInkView?.ComponentView?.snapPt?.(screenDragPt, doc === inkView.rootDoc ? this.excludeSelfSnapSegs(ink, controlIndex) : []); if (snapped && snapped.distance < snapData.distance) { const snappedInkPt = doc === inkView.rootDoc ? snapped.nearestPt : inkView.ComponentView?.ptFromScreen?.(testInkView?.ComponentView?.ptToScreen?.(snapped.nearestPt) ?? { X: 0, Y: 0 }); // convert from snapped ink coordinate system to dragged ink coordinate system by converting to/from screen space diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 976c8763e..69eec8456 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -266,8 +266,6 @@ export class LightboxView extends React.Component { docFilters={this.docFilters} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} addDocument={undefined} removeDocument={undefined} whenChildContentsActiveChanged={emptyFunction} @@ -275,7 +273,7 @@ export class LightboxView extends React.Component { pinToPres={TabDocView.PinDoc} bringToFront={emptyFunction} onBrowseClick={MainView.Instance.exploreMode} - focus={DocUtils.DefaultFocus} + focus={emptyFunction} />
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 60459cf30..0384d925e 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -595,14 +595,12 @@ export class MainView extends React.Component { PanelWidth={this.headerBarDocWidth} PanelHeight={this.headerBarDocHeight} renderDepth={0} - focus={DocUtils.DefaultFocus} + focus={emptyFunction} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} />
); @@ -626,14 +624,12 @@ export class MainView extends React.Component { ScreenToLocalTransform={this._hideUI ? this.mainScreenToLocalXf : Transform.Identity} PanelWidth={this.mainDocViewWidth} PanelHeight={this.mainDocViewHeight} - focus={DocUtils.DefaultFocus} + focus={emptyFunction} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} suppressSetHeight={true} renderDepth={this._hideUI ? 0 : -1} /> @@ -727,14 +723,12 @@ export class MainView extends React.Component { renderDepth={0} isContentActive={returnTrue} scriptContext={CollectionDockingView.Instance?.props.Document} - focus={DocUtils.DefaultFocus} + focus={emptyFunction} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} />
{this.docButtons} @@ -758,7 +752,7 @@ export class MainView extends React.Component { PanelHeight={this.leftMenuHeight} renderDepth={0} docViewPath={returnEmptyDoclist} - focus={DocUtils.DefaultFocus} + focus={emptyFunction} styleProvider={DefaultStyleProvider} isContentActive={returnTrue} whenChildContentsActiveChanged={emptyFunction} @@ -766,8 +760,6 @@ export class MainView extends React.Component { docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} scriptContext={this} />
@@ -900,13 +892,11 @@ export class MainView extends React.Component { PanelWidth={this.leftMenuFlyoutWidth} PanelHeight={this.leftMenuFlyoutHeight} renderDepth={0} - focus={DocUtils.DefaultFocus} + focus={emptyFunction} whenChildContentsActiveChanged={emptyFunction} docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} /> {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ?
{StrCast(this.userDoc?.presentationMode)}
: <>}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index ec22128d4..bdc48d03a 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -1,4 +1,4 @@ -import { action, computed, observable, trace } from 'mobx'; +import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import * as React from 'react'; @@ -10,15 +10,13 @@ import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, retu import { DocUtils } from '../documents/Documents'; import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from '../util/DragManager'; -import { ScriptingGlobals } from '../util/ScriptingGlobals'; import { Transform } from '../util/Transform'; import { CollectionFreeFormLinksView } from './collections/collectionFreeForm/CollectionFreeFormLinksView'; import { LightboxView } from './LightboxView'; import { MainView } from './MainView'; import { DocumentView } from './nodes/DocumentView'; import './OverlayView.scss'; -import { ScriptingRepl } from './ScriptingRepl'; -import { DefaultStyleProvider, testDocProps } from './StyleProvider'; +import { DefaultStyleProvider } from './StyleProvider'; export type OverlayDisposer = () => void; @@ -223,7 +221,7 @@ export class OverlayView extends React.Component { isDocumentActive={returnTrue} isContentActive={returnTrue} whenChildContentsActiveChanged={emptyFunction} - focus={DocUtils.DefaultFocus} + focus={emptyFunction} styleProvider={DefaultStyleProvider} docViewPath={returnEmptyDoclist} addDocTab={d.type === DocumentType.PRES ? MainView.addDocTabFunc : returnFalse} @@ -231,8 +229,6 @@ export class OverlayView extends React.Component { docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} />
); diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx index 954529bc9..3ad28c418 100644 --- a/src/client/views/Palette.tsx +++ b/src/client/views/Palette.tsx @@ -1,12 +1,12 @@ -import { IReactionDisposer, observable, reaction } from "mobx"; -import { observer } from "mobx-react"; -import * as React from "react"; -import { Doc } from "../../fields/Doc"; -import { NumCast } from "../../fields/Types"; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, emptyPath } from "../../Utils"; -import { Transform } from "../util/Transform"; -import { DocumentView } from "./nodes/DocumentView"; -import "./Palette.scss"; +import { IReactionDisposer, observable, reaction } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { Doc } from '../../fields/Doc'; +import { NumCast } from '../../fields/Types'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, emptyPath } from '../../Utils'; +import { Transform } from '../util/Transform'; +import { DocumentView } from './nodes/DocumentView'; +import './Palette.scss'; export interface PaletteProps { x: number; @@ -23,20 +23,20 @@ export default class Palette extends React.Component { componentDidMount = () => { this._selectedDisposer = reaction( () => NumCast(this.props.thumbDoc.selectedIndex), - (i) => this._selectedIndex = i, + i => (this._selectedIndex = i), { fireImmediately: true } ); - } + }; componentWillUnmount = () => { this._selectedDisposer?.(); - } + }; render() { return (
-
+
{ docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} /> + />
); } -} \ No newline at end of file +} diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx index 2c7da5931..93eec61f3 100644 --- a/src/client/views/PropertiesDocContextSelector.tsx +++ b/src/client/views/PropertiesDocContextSelector.tsx @@ -22,9 +22,9 @@ export class PropertiesDocContextSelector extends React.Component alias.context && alias.context instanceof Doc && Cast(alias.context, Doc, null) !== targetContext).reduce((set, alias) => set.add(Cast(alias.context, Doc, null)), new Set()); + const containerProtos = aliases.filter(alias => alias.context && alias.context instanceof Doc).reduce((set, alias) => set.add(Cast(alias.context, Doc, null)), new Set()); const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.aliases)); const containers = containerSets.reduce((p, set) => { set.map(s => p.add(s)); @@ -42,6 +42,7 @@ export class PropertiesDocContextSelector extends React.Component !Doc.AreProtosEqual(doc, CollectionDockingView.Instance?.props.Document)) .filter(doc => !Doc.IsSystem(doc)) + .filter(doc => doc !== targetContext) .map(doc => ({ col: doc, target })); } @@ -53,8 +54,21 @@ export class PropertiesDocContextSelector extends React.Component DocFocusOrOpen(Doc.GetProto(this.props.DocView!.props.Document), col), 100); + //this.props.addDocTab(col, (OpenWhere.toggle + ':' + OpenWhereMod.right) as OpenWhere); + setTimeout( + () => + this.props.DocView && + DocFocusOrOpen( + Doc.GetProto(this.props.DocView.props.Document), + { + // + willZoomCentered: true, + openLocation: (OpenWhere.toggle + ':' + OpenWhereMod.right) as OpenWhere, + }, + col + ), + 100 + ); }; render() { diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index fbc7d7696..6582c3f2a 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -341,8 +341,6 @@ export class PropertiesView extends React.Component { docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionDoc={undefined} - ContainingCollectionView={undefined} addDocument={returnFalse} moveDocument={undefined} removeDocument={returnFalse} diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index b950b4860..739d6d819 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -18,7 +18,7 @@ import { TreeSort } from './collections/TreeView'; import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; import { MainView } from './MainView'; -import { DocumentViewProps } from './nodes/DocumentView'; +import { DocumentViewProps, OpenWhere } from './nodes/DocumentView'; import { FieldViewProps } from './nodes/FieldView'; import { KeyValueBox } from './nodes/KeyValueBox'; import { SliderBox } from './nodes/SliderBox'; @@ -295,7 +295,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt, props: Opt { - if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform) { + if (props?.docViewPath().lastElement()?.rootDoc?._viewType === CollectionViewType.Freeform) { return doc?.pointerEvents !== 'none' ? null : (
toggleLockedPosition(doc)}> @@ -383,10 +383,7 @@ export function DashboardStyleProvider(doc: Opt, props: Opt {DashboardToggleButton(doc, 'hidden', 'eye-slash', 'eye', () => { - doc.hidden = doc.hidden ? undefined : true; - if (!doc.hidden) { - DocFocusOrOpen(doc, props?.ContainingCollectionDoc); - } + DocFocusOrOpen(doc, { toggleTarget: true, willPan: true, openLocation: OpenWhere.addRight }, props?.docViewPath().lastElement()?.rootDoc); })} ); diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 45db240a9..c5a501aa6 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -114,8 +114,6 @@ export class TemplateMenu extends React.Component { { docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} />
); @@ -642,8 +640,8 @@ export class CollectionViewBaseChrome extends React.Component this.props.docView.props.CollectionFreeFormDocumentView?.().float())}> diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index aec0734b4..80e81bc1c 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -255,8 +255,6 @@ export class CollectionNoteTakingView extends CollectionSubView() { hideTitle={this.props.childHideTitle?.()} docRangeFilters={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} - ContainingCollectionDoc={this.props.CollectionView?.props.Document} - ContainingCollectionView={this.props.CollectionView} addDocument={this.props.addDocument} moveDocument={this.props.moveDocument} removeDocument={this.props.removeDocument} @@ -632,8 +630,6 @@ export class CollectionNoteTakingView extends CollectionSubView() { docFilters={this.props.docFilters} docRangeFilters={this.props.docRangeFilters} searchFilterDocs={this.props.searchFilterDocs} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} />
); diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index ba90ed8cd..fd9b0c0ce 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -30,7 +30,7 @@ export class CollectionPileView extends CollectionSubView() { // pileups are designed to go away when they are empty. this._disposers.selected = reaction( () => this.childDocs.length, - num => !num && this.props.ContainingCollectionView?.removeDocument(this.props.Document) + num => !num && this.props.CollectionFreeFormDocumentView?.().props.removeDocument?.(this.props.Document) ); } componentWillUnmount() { diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index bbd81d06d..22a575989 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -815,8 +815,6 @@ class StackedTimelineAnchor extends React.Component whenChildContentsActiveChanged={emptyFunction} focus={focusFunc} isContentActive={returnFalse} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} searchFilterDocs={returnEmptyDoclist} docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index a85ee0e02..67f5dc9f4 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -346,8 +346,6 @@ export class CollectionStackingView extends CollectionSubView ); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 4a11e8f0b..92932fb61 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -164,7 +164,7 @@ export class CollectionTreeView extends CollectionSubView 0) { FormattedTextBox.SelectOnLoad = prev[Id]; - DocumentManager.Instance.getDocumentView(prev, this.props.CollectionView)?.select(false); + DocumentManager.Instance.getDocumentView(prev, this.props.DocumentView?.())?.select(false); } return true; } @@ -242,8 +242,6 @@ export class CollectionTreeView extends CollectionSubView ); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index bc25ad43a..53fbcc3cc 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -279,7 +279,10 @@ export class CollectionView extends ViewBoxAnnotatableComponent +
{this.showIsTagged()} {this.renderSubView(this.collectionViewType, props)}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 458712999..ef8f395c5 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -425,8 +425,6 @@ export class TabDocView extends React.Component { hideTitle={this.props.keyValue} Document={this._document} DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} onBrowseClick={MainView.Instance.exploreMode} waitForDoubleClickToClick={MainView.Instance.waitForDoubleClick} isContentActive={returnTrue} @@ -580,8 +578,6 @@ export class TabMinimapView extends React.Component { { ScreenToLocalTransform={Transform.Identity} renderDepth={0} whenChildContentsActiveChanged={emptyFunction} - focus={DocUtils.DefaultFocus} + focus={emptyFunction} styleProvider={TabMinimapView.miniStyleProvider} addDocTab={this.props.addDocTab} pinToPres={TabDocView.PinDoc} diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 75e76019e..8b1dfc767 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -42,7 +42,7 @@ export interface TreeViewProps { prevSibling?: Doc; document: Doc; dataDoc?: Doc; - containerCollection: Doc; + treeViewParent: Doc; renderDepth: number; dropAction: dropActionType; addDocTab: (doc: Doc, where: OpenWhere) => boolean; @@ -143,7 +143,7 @@ export class TreeView extends React.Component { return this.validExpandViewTypes.includes(StrCast(this.doc.treeViewExpandedView)) ? StrCast(this.doc.treeViewExpandedView) : this.defaultExpandedView; } @computed get MAX_EMBED_HEIGHT() { - return NumCast(this.props.containerCollection.maxEmbedHeight, 200); + return NumCast(this.props.treeViewParent.maxEmbedHeight, 200); } @computed get dataDoc() { return this.props.document.treeViewChildrenOnRoot ? this.doc : this.doc[DataSym]; @@ -194,7 +194,7 @@ export class TreeView extends React.Component { const ind = this.dataDoc[key].indexOf(doc); const res = (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true); - res && ind > 0 && DocumentManager.Instance.getDocumentView(this.dataDoc[key][ind - 1], this.props.treeView.props.CollectionView)?.select(false); + res && ind > 0 && DocumentManager.Instance.getDocumentView(this.dataDoc[key][ind - 1], this.props.treeView.props.DocumentView?.())?.select(false); return res; }; @@ -385,7 +385,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.containerCollection)?.freezeChildren).includes('add')) || forceAdd; + const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.freezeChildren).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)); @@ -406,7 +406,7 @@ export class TreeView extends React.Component { getTransform = () => this.refTransform(this._tref.current); embeddedPanelWidth = () => this.props.panelWidth() / (this.props.treeView.props.NativeDimScaling?.() || 1); embeddedPanelHeight = () => { - const layoutDoc = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc; + const layoutDoc = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc; return Math.min( layoutDoc[HeightSym](), this.MAX_EMBED_HEIGHT, @@ -416,7 +416,7 @@ export class TreeView extends React.Component { return layoutDoc._fitWidth ? !Doc.NativeHeight(layoutDoc) ? NumCast(layoutDoc._height) - : Math.min((this.embeddedPanelWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containerCollection._height))) + : Math.min((this.embeddedPanelWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.treeViewParent._height))) : (this.embeddedPanelWidth() * layoutDoc[HeightSym]()) / layoutDoc[WidthSym](); })() ); @@ -452,7 +452,7 @@ export class TreeView extends React.Component { this, doc, undefined, - this.props.containerCollection, + this.props.treeViewParent, this.props.prevSibling, addDoc, remDoc, @@ -596,7 +596,7 @@ export class TreeView extends React.Component { this, this.layoutDoc, this.dataDoc, - this.props.containerCollection, + this.props.treeViewParent, this.props.prevSibling, addDoc, remDoc, @@ -656,7 +656,7 @@ export class TreeView extends React.Component { this.onCheckedClick?.script.run( { this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc, - heading: this.props.containerCollection.title, + heading: this.props.treeViewParent.title, checked: this.doc.treeViewChecked === 'check' ? 'x' : this.doc.treeViewChecked === 'x' ? 'remove' : 'check', containingTreeView: this.props.treeView.props.Document, }, @@ -724,9 +724,11 @@ export class TreeView extends React.Component { }; @observable headerEleWidth = 0; - @computed get headerElements() { + @computed get titleButtons() { + const customHeaderButtons = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Decorations); return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? null : ( <> + {customHeaderButtons} {/* e.g.,. hide button is set by dashboardStyleProvider */} {this.doc.hideContextMenu ? null : ( { return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label })); }; - onChildClick = () => { - return this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!); - }; + onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!); onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick, !this.props.treeView.outlineMode ? this._openScript?.() : null); @@ -906,7 +906,7 @@ export class TreeView extends React.Component { styleProvider={this.titleStyleProvider} enableDragWhenActive={true} onClickScriptDisable="never" // tree docViews have a script to show fields, etc. - docViewPath={returnEmptyDoclist} + docViewPath={this.props.treeView.props.docViewPath} treeViewDoc={this.props.treeView.props.Document} addDocument={undefined} addDocTab={this.props.addDocTab} @@ -937,12 +937,8 @@ export class TreeView extends React.Component { docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={this.props.treeView.props.CollectionView} - ContainingCollectionDoc={this.props.treeView.props.Document} /> ); - - const buttons = this.props.styleProvider?.(this.doc, { ...this.props.treeView.props, ContainingCollectionDoc: this.props.parentTreeView?.doc }, StyleProp.Decorations + (Doc.IsSystem(this.props.containerCollection) ? ':afterHeader' : '')); return ( <>
{ {view}
r && (this.headerEleWidth = r.getBoundingClientRect().width))}> - {buttons} {/* hide and lock buttons */} - {this.headerElements} + {this.titleButtons}
); @@ -1017,8 +1012,6 @@ export class TreeView extends React.Component { docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionDoc={this.props.containerCollection} - ContainingCollectionView={undefined} addDocument={this.props.addDocument} moveDocument={this.move} removeDocument={this.props.removeDoc} @@ -1131,7 +1124,7 @@ export class TreeView extends React.Component { childDocs: Doc[], treeView: CollectionTreeView, parentTreeView: CollectionTreeView | TreeView | undefined, - containerCollection: Doc, + treeViewParent: Doc, dataDoc: Doc | undefined, parentCollectionDoc: Doc | undefined, containerPrevSibling: Doc | undefined, @@ -1162,19 +1155,19 @@ export class TreeView extends React.Component { hierarchyIndex?: number[], renderCount?: number ) { - const viewSpecScript = Cast(containerCollection.viewSpecScript, ScriptField); + const viewSpecScript = Cast(treeViewParent.viewSpecScript, ScriptField); if (viewSpecScript) { childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result); } - const docs = TreeView.sortDocs(childDocs, StrCast(containerCollection.treeViewSortCriterion, TreeSort.None)); + const docs = TreeView.sortDocs(childDocs, StrCast(treeViewParent.treeViewSortCriterion, TreeSort.None)); const rowWidth = () => panelWidth() - treeBulletWidth() * (treeView.props.NativeDimScaling?.() || 1); const treeViewRefs = new Map(); return docs .filter(child => child instanceof Doc) .map((child, i) => { if (renderCount && i > renderCount) return null; - const pair = Doc.GetLayoutDataDocPair(containerCollection, dataDoc, child); + const pair = Doc.GetLayoutDataDocPair(treeViewParent, dataDoc, child); if (!pair.layout || pair.data instanceof Promise) { return null; } @@ -1192,10 +1185,7 @@ export class TreeView extends React.Component { } }; const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1])); - const outdent = - parentCollectionDoc?._viewType !== CollectionViewType.Tree - ? undefined - : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined); + const outdent = !parentCollectionDoc ? undefined : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined); const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false); const childLayout = Doc.Layout(pair.layout); const rowHeight = () => { @@ -1208,7 +1198,7 @@ export class TreeView extends React.Component { ref={r => treeViewRefs.set(child, r ? r : undefined)} document={pair.layout} dataDoc={pair.data} - containerCollection={containerCollection} + treeViewParent={treeViewParent} prevSibling={docs[i]} // TODO: [AL] add these hierarchyIndex={hierarchyIndex ? [...hierarchyIndex, i + 1] : undefined} @@ -1220,7 +1210,7 @@ export class TreeView extends React.Component { onCheckedClick={onCheckedClick} onChildClick={onChildClick} renderDepth={renderDepth} - removeDoc={StrCast(containerCollection.freezeChildren).includes('remove') ? undefined : remove} + removeDoc={StrCast(treeViewParent.freezeChildren).includes('remove') ? undefined : remove} addDocument={addDocument} styleProvider={styleProvider} panelWidth={rowWidth} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 7ae7be3c8..a3f5e73fb 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -262,7 +262,7 @@ export class CollectionFreeFormView extends CollectionSubView { SelectionManager.DeselectAll(); - docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)).map(dv => dv && SelectionManager.SelectView(dv, true)); + docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.DocumentView?.())).map(dv => dv && SelectionManager.SelectView(dv, true)); }; addDocument = (newBox: Doc | Doc[]) => { let retVal = false; @@ -319,7 +319,7 @@ export class CollectionFreeFormView extends CollectionSubView 1 ? NumCast(refDoc.y) - (NumCast(sorted[0].layout.y) + (topIndexed ? 0 : NumCast(sorted[0].layout._height))) : 0; const deltax = sorted.length > 1 ? NumCast(refDoc.x) - NumCast(sorted[0].layout.x) : 0; let lastx = NumCast(refDoc.x); let lasty = NumCast(refDoc.y) + (topIndexed ? 0 : NumCast(refDoc._height)); - setTimeout( - action(() => - sorted.slice(1).forEach((pair, i) => { - lastx = pair.layout.x = lastx + deltax; - lasty = (pair.layout.y = lasty + deltay) + (topIndexed ? 0 : NumCast(pair.layout._height)); - }) - ) + runInAction(() => + sorted.slice(1).forEach((pair, i) => { + lastx = pair.layout.x = lastx + deltax; + lasty = (pair.layout.y = lasty + deltay) + (topIndexed ? 0 : NumCast(pair.layout._height)); + }) ); } } @@ -464,7 +462,7 @@ export class CollectionFreeFormView extends CollectionSubView pair.layout).filter(cd => (this.props.Document._useClusters ? NumCast(cd.cluster) : NumCast(cd.group, -1)) === cluster); - const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.CollectionView)!); + const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.DocumentView?.())!); const { left, top } = clusterDocs[0].getBounds() || { left: 0, top: 0 }; const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? 'alias' : undefined); de.moveDocument = this.props.moveDocument; @@ -872,7 +870,7 @@ export class CollectionFreeFormView extends CollectionSubView DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)) + .map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.DocumentView?.())) .filter(inkView => inkView?.ComponentView instanceof InkingStroke) .map(inkView => ({ inkViewBounds: inkView!.getBounds(), inkStroke: inkView!.ComponentView as InkingStroke, inkView: inkView! })) .filter( @@ -966,7 +964,7 @@ export class CollectionFreeFormView extends CollectionSubView doc.type === DocumentType.INK && !doc.dontIntersect) .forEach(doc => { - const otherInk = DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)?.ComponentView as InkingStroke; + const otherInk = DocumentManager.Instance.getDocumentView(doc, this.props.DocumentView?.())?.ComponentView as InkingStroke; const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] }; const otherScreenPts = otherInkData.map(point => otherInk.ptToScreen(point)); const otherCtrlPts = otherScreenPts.map(spt => (ink.ComponentView as InkingStroke).ptFromScreen(spt)); @@ -1121,7 +1119,8 @@ export class CollectionFreeFormView extends CollectionSubView { - if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform || this.props.ContainingCollectionDoc._panX !== undefined) { + const collectionDoc = this.props.docViewPath().lastElement().rootDoc; + if (collectionDoc?._viewType !== CollectionViewType.Freeform || collectionDoc._panX !== undefined) { this.setPan( NumCast(this.layoutDoc[this.panXFieldKey]) + ((this.props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale NumCast(this.layoutDoc[this.panYFieldKey]) + ((this.props.PanelHeight() / 2) * -y) / this.zoomScaling(), @@ -1256,8 +1255,6 @@ export class CollectionFreeFormView extends CollectionSubView @@ -251,8 +249,7 @@ export class CollectionLinearView extends CollectionSubView() { self: this.rootDoc, _readOnly_: false, scriptContext: this.props.scriptContext, - thisContainer: this.props.ContainingCollectionDoc, - documentView: this.props.docViewPath().lastElement(), + documentView: this.props.DocumentView?.(), }); this.layoutDoc.linearViewIsExpanded = this.addMenuToggle.current!.checked; })} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index b73b1d779..78d3d1b6e 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -262,8 +262,6 @@ export class CollectionMulticolumnView extends CollectionSubView() { docFilters={this.childDocFilters} docRangeFilters={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} - ContainingCollectionDoc={this.props.CollectionView?.props.Document} - ContainingCollectionView={this.props.CollectionView} dontRegisterView={this.props.dontRegisterView} addDocument={this.props.addDocument} moveDocument={this.props.moveDocument} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 0cca83803..4d61dc272 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -261,8 +261,6 @@ export class CollectionMultirowView extends CollectionSubView() { docFilters={this.childDocFilters} docRangeFilters={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} - ContainingCollectionDoc={this.props.CollectionView?.props.Document} - ContainingCollectionView={this.props.CollectionView} dontRegisterView={this.props.dontRegisterView} addDocument={this.props.addDocument} moveDocument={this.props.moveDocument} diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index fd9bcf681..6d5a73e55 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -793,7 +793,7 @@ export class CollectionSchemaView extends CollectionSubView() { fitContentsToBox={returnTrue} dontCenter={'y'} onClickScriptDisable="always" - focus={DocUtils.DefaultFocus} + focus={emptyFunction} renderDepth={this.props.renderDepth + 1} rootSelected={this.rootSelected} PanelWidth={this.previewWidthFunc} @@ -806,8 +806,6 @@ export class CollectionSchemaView extends CollectionSubView() { searchFilterDocs={this.searchFilterDocs} styleProvider={DefaultStyleProvider} docViewPath={returnEmptyDoclist} - ContainingCollectionDoc={this.props.CollectionView?.props.Document} - ContainingCollectionView={this.props.CollectionView} moveDocument={this.props.moveDocument} addDocument={this.addRow} removeDocument={this.props.removeDocument} @@ -849,8 +847,6 @@ class CollectionSchemaViewDocs extends React.Component() { } @computed get schemaDoc() { - return this.props.ContainingCollectionDoc!; + return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc; } @computed get rowIndex() { diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 13e45963e..5f8ffe8b0 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -28,8 +28,6 @@ export class SchemaTableCell extends React.Component { searchFilterDocs: returnEmptyDoclist, styleProvider: DefaultStyleProvider, docViewPath: returnEmptyDoclist, - ContainingCollectionView: undefined, - ContainingCollectionDoc: undefined, fieldKey: this.props.fieldKey, rootSelected: returnFalse, isSelected: returnFalse, diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 29e7cd3ad..d703c9595 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -44,25 +44,10 @@ export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: Docume } const dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs); - dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => { - docView.props.removeDocument?.(doc); - addDocument(doc); - return true; - }; - const containingView = docView.props.ContainingCollectionView; - const finishDrag = (e: DragManager.DragCompleteEvent) => - e.docDragData && - (e.docDragData.droppedDocuments = dragData.draggedDocuments.reduce((droppedDocs, d) => { - const dvs = DocumentManager.Instance.getDocumentViews(d).filter(dv => dv.props.ContainingCollectionView === containingView); - if (dvs.length) { - dvs.forEach(dv => droppedDocs.push(dv.props.Document)); - } else { - droppedDocs.push(Doc.MakeAlias(d)); - } - return droppedDocs; - }, [] as Doc[])); + dragData.canEmbed = true; + dragData.moveDocument = (docView.props.docViewPath().lastElement()?.ComponentView as any)?.props.CollectionView?.moveDocument; // this is equal to docView.props.moveDocument, but moveDocument is not a defined prop of a DocumentView .. but maybe it should be? - DragManager.StartDrag([dragEle], dragData, downX, downY, undefined, finishDrag); + DragManager.StartDocumentDrag([dragEle], dragData, downX, downY, undefined); } } diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx index 7bdace2b6..5f2d4a7b6 100644 --- a/src/client/views/linking/LinkPopup.tsx +++ b/src/client/views/linking/LinkPopup.tsx @@ -85,15 +85,13 @@ export class LinkPopup extends React.Component { PanelWidth={this.getPWidth} PanelHeight={this.getPHeight} renderDepth={0} - focus={DocUtils.DefaultFocus} + focus={emptyFunction} docViewPath={returnEmptyDoclist} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} /> diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 9bdb2cee7..24b9f3b25 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -160,8 +160,9 @@ export class CollectionFreeFormDocumentView extends DocComponent { - const { Document: topDoc, ContainingCollectionView: container } = this.props; - const screenXf = container?.screenToLocalTransform(); + const topDoc = this.rootDoc; + const containerDocView = this.props.docViewPath().lastElement(); + const screenXf = containerDocView?.screenToLocalTransform(); if (screenXf) { SelectionManager.DeselectAll(); if (topDoc.z) { @@ -178,7 +179,7 @@ export class CollectionFreeFormDocumentView extends DocComponent SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(topDoc, container)!, false), 0); + setTimeout(() => SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(topDoc, containerDocView), false), 0); } }; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index dbcfe43cf..76a5ce7b3 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -154,7 +154,6 @@ export class DocumentContentsView extends React.Component< // these are the properties in DocumentViewProps that need to be removed to pass on only DocumentSharedViewProps to the FieldViews 'hideResizeHandles', 'hideTitle', - 'treeViewDoc', 'contentPointerEvents', 'radialMenu', 'LayoutTemplateString', diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index df3299eef..47705d53d 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -142,7 +142,7 @@ export class DocumentLinksButton extends React.Component undefined | { x: number; y: number; r: number; b: number }; fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitContentsToBox property on a Document - ContainingCollectionView: Opt; - ContainingCollectionDoc: Opt; suppressSetHeight?: boolean; thumbShown?: () => boolean; setContentView?: (view: DocComponentView) => any; @@ -179,6 +177,7 @@ export interface DocumentViewSharedProps { ScreenToLocalTransform: () => Transform; bringToFront: (doc: Doc, sendToBack?: boolean) => void; canEmbedOnDrag?: boolean; + treeViewDoc?: Doc; xPadding?: number; yPadding?: number; dropAction?: dropActionType; @@ -210,7 +209,6 @@ export interface DocumentViewProps extends DocumentViewSharedProps { hideOpenButton?: boolean; hideDeleteButton?: boolean; hideLinkAnchors?: boolean; - treeViewDoc?: Doc; isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events isContentActive: () => boolean | undefined; // whether document contents should handle pointer events 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 @@ -429,7 +427,6 @@ export class DocumentViewInternal extends DocComponent this.props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' }); !appearance && appearanceItems.length && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' }); - if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) { + if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.docViewPath().lastElement()?.rootDoc?._viewType !== CollectionViewType.Tree) { const existingOnClick = cm.findByDescription('OnClick...'); const onClicks: ContextMenuProps[] = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : []; @@ -1446,7 +1442,7 @@ export class DocumentView extends React.Component { scaleToScreenSpace = () => (1 / (this.props.NativeDimScaling?.() || 1)) * this.screenToLocalTransform().Scale; docViewPathFunc = () => this.docViewPath; isSelected = (outsideReaction?: boolean) => SelectionManager.IsSelected(this, outsideReaction); - select = (extendSelection: boolean) => SelectionManager.SelectView(this, !SelectionManager.Views().some(v => v.props.Document === this.props.ContainingCollectionDoc) && extendSelection); + select = (extendSelection: boolean) => SelectionManager.SelectView(this, extendSelection); NativeWidth = () => this.effectiveNativeWidth; NativeHeight = () => this.effectiveNativeHeight; PanelWidth = () => this.panelWidth; diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 8d3534a5c..86779e0dd 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -32,6 +32,7 @@ export interface FieldViewProps extends DocumentViewSharedProps { // See currentUserUtils headerTemplate for examples of creating text boxes from html which set some of these fields // Also, see InkingStroke for examples of creating text boxes from render() methods which set some of these fields backgroundColor?: string; + treeViewDoc?: Doc; color?: string; fontSize?: number; height?: number; diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 94434dce7..7ea6d42ff 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -61,8 +61,6 @@ export class KeyValuePair extends React.Component { searchFilterDocs: returnEmptyDoclist, styleProvider: DefaultStyleProvider, docViewPath: returnEmptyDoclist, - ContainingCollectionView: undefined, - ContainingCollectionDoc: undefined, fieldKey: this.props.keyName, rootSelected: returnFalse, isSelected: returnFalse, diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index fcc5b6975..c58b5dd8c 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -271,14 +271,12 @@ export class LinkDocPreview extends React.Component { docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - ContainingCollectionDoc={undefined} - ContainingCollectionView={undefined} renderDepth={0} suppressSetHeight={true} PanelWidth={this.width} PanelHeight={this.height} pointerEvents={returnNone} - focus={DocUtils.DefaultFocus} + focus={emptyFunction} whenChildContentsActiveChanged={returnFalse} ignoreAutoHeight={true} // need to ignore autoHeight otherwise autoHeight text boxes will expand beyond the preview panel size. bringToFront={returnFalse} diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index db11a7776..e015024fd 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -305,8 +305,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent + renderDepth={this.props.renderDepth + 1}> <> {this.threed} {this.content} @@ -330,7 +329,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent + /> )} diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 8eacfbc51..1a75a7e76 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -568,12 +568,12 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b if (selectedViews.length) { if (checkResult) { const selView = selectedViews.lastElement(); - const layoutFrameNumber = Cast(selView.props.ContainingCollectionDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values + const layoutFrameNumber = Cast(selView.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values const contentFrameNumber = Cast(selView.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed return CollectionFreeFormDocumentView.getStringValues(selView?.rootDoc, contentFrameNumber).backgroundColor ?? 'transparent'; } selectedViews.forEach(dv => { - const layoutFrameNumber = Cast(dv.props.ContainingCollectionDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values + const layoutFrameNumber = Cast(dv.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values const contentFrameNumber = Cast(dv.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed if (contentFrameNumber !== undefined) { CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, { backgroundColor: color }); diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index c00ab6a7e..b31fc01ff 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -9,10 +9,9 @@ import { DocServer } from '../../../DocServer'; import { Docs, DocUtils } from '../../../documents/Documents'; import { ColorScheme } from '../../../util/SettingsManager'; import { Transform } from '../../../util/Transform'; -import { DocumentView } from '../DocumentView'; +import { DocFocusOptions, DocumentView } from '../DocumentView'; import { FormattedTextBox } from './FormattedTextBox'; import React = require('react'); -import { SelectionManager } from '../../../util/SelectionManager'; export class DashDocView { dom: HTMLSpanElement; // container for label and value @@ -151,7 +150,7 @@ export class DashDocViewInternal extends React.Component { const { scale, translateX, translateY } = Utils.GetScreenTransform(this._spanRef.current); return new Transform(-translateX, -translateY, 1).scale(1 / scale); }; - outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document, {}); // ideally, this would scroll to show the focus target + outerFocus = (target: Doc, options: DocFocusOptions) => this._textBox.focus(target, options); // ideally, this would scroll to show the focus target onKeyDown = (e: any) => { e.stopPropagation(); @@ -212,8 +211,6 @@ export class DashDocViewInternal extends React.Component { docFilters={this.props.tbox?.props.docFilters} docRangeFilters={this.props.tbox?.props.docRangeFilters} searchFilterDocs={this.props.tbox?.props.searchFilterDocs} - ContainingCollectionView={this._textBox.props.ContainingCollectionView} - ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc} /> ); diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index f23426bb3..c43206629 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -235,10 +235,7 @@ export class DashFieldViewInternal extends React.Component { - let container = this.props.tbox.props.ContainingCollectionView; - while (container?.props.Document.isTemplateForField || container?.props.Document.isTemplateDoc) { - container = container.props.ContainingCollectionView; - } + let container = this.props.tbox.props.DocumentView?.().props.docViewPath().lastElement(); if (container) { const alias = Doc.MakeAlias(container.props.Document); alias._viewType = CollectionViewType.Time; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 677c4662b..bc2a5d797 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -541,7 +541,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - const tanch = Docs.Create.TextanchorDocument({ title: 'dictation anchor' }); + const tanch = Docs.Create.TextanchorDocument({ title: 'dictation anchor', unrendered: true }); return this.addDocument(tanch) ? tanch : undefined; }; const link = DocUtils.MakeLinkToActiveAudio(textanchorFunc, false).lastElement(); @@ -956,7 +957,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { const examinedNode = findAnchorNode(node, editor); - if (examinedNode?.node && (examinedNode.node.textContent || examinedNode.node.type === this._editorView?.state.schema.nodes.audiotag)) { + if (examinedNode?.node && (examinedNode.node.textContent || examinedNode.node.type === this._editorView?.state.schema.nodes.dashDoc || examinedNode.node.type === this._editorView?.state.schema.nodes.audiotag)) { nodes.push(examinedNode.node); !hadStart && (start = index + examinedNode.start); hadStart = true; @@ -971,9 +972,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent mark.type === editor.state.schema.marks.linkAnchor); @@ -987,7 +994,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent 2 || (content?.length && content[0].type === this._editorView.state.schema.nodes.audiotag)) && ret.start >= 0) { + if ((ret.frag.size || (content?.length && content[0].type === this._editorView.state.schema.nodes.dashDoc) || (content?.length && content[0].type === this._editorView.state.schema.nodes.audiotag)) && ret.start >= 0) { !options.instant && (this._focusSpeed = focusSpeed); let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start if (ret.frag.firstChild) { @@ -998,6 +1005,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent (this._focusSpeed = undefined), this._focusSpeed); setTimeout(() => clearStyleSheetRules(FormattedTextBox._highlightStyleSheet), Math.max(this._focusSpeed || 0, 3000)); + return focusSpeed; + } else { + return this.props.focus(this.rootDoc, options); } } }; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 3589a9065..807a19771 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -2457,7 +2457,6 @@ export class PresBox extends ViewBoxBaseComponent() { {mode !== CollectionViewType.Invalid ? ( () { // Idea: this boolean will determine whether to automatically show the video when this preselement is selected. // @observable static showVideo: boolean = false; @computed get indexInPres() { - return DocListCast(this.presBox[StrCast(this.presBox.presFieldKey, 'data')]).indexOf(this.rootDoc); + return DocListCast(this.presBox?.[StrCast(this.presBox.presFieldKey, 'data')]).indexOf(this.rootDoc); } // the index field is where this document is in the presBox display list (since this value is different for each presentation element, the value can't be stored on the layout template which is used by all display elements) @computed get expandViewHeight() { return 100; @@ -51,11 +51,10 @@ export class PresElementBox extends ViewBoxBaseComponent() { return this.presBoxView?.selectedArray; } @computed get presBoxView() { - const vpath = this.props.docViewPath(); - return vpath.length > 1 ? (vpath[vpath.length - 2].ComponentView as PresBox) : undefined; + return this.props.DocumentView?.()?.props.docViewPath().lastElement()?.ComponentView as PresBox; } @computed get presBox() { - return this.props.ContainingCollectionDoc!; + return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc; } @computed get targetDoc() { return Cast(this.rootDoc.presentationTargetDoc, Doc, null) || this.rootDoc; @@ -110,14 +109,12 @@ export class PresElementBox extends ViewBoxBaseComponent() { docFilters={this.props.docFilters} docRangeFilters={this.props.docRangeFilters} searchFilterDocs={this.props.searchFilterDocs} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} rootSelected={returnTrue} addDocument={returnFalse} removeDocument={returnFalse} fitContentsToBox={returnTrue} moveDocument={this.props.moveDocument!} - focus={DocUtils.DefaultFocus} + focus={emptyFunction} whenChildContentsActiveChanged={returnFalse} addDocTab={returnFalse} pinToPres={returnFalse} @@ -195,7 +192,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { const dragData = new DragManager.DocumentDragData(this.presBoxView?.sortArray() ?? []); if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.rootDoc); dragData.dropAction = 'move'; - dragData.treeViewDoc = this.presBox._viewType === CollectionViewType.Tree ? this.props.ContainingCollectionDoc : undefined; // this.props.DocumentView?.()?.props.treeViewDoc; + dragData.treeViewDoc = this.presBox?._viewType === CollectionViewType.Tree ? this.presBox : undefined; // this.props.DocumentView?.()?.props.treeViewDoc; dragData.moveDocument = this.props.moveDocument; const dragItem: HTMLElement[] = []; if (dragArray.length === 1) { @@ -269,7 +266,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { @undoBatch removeItem = action((e: React.MouseEvent) => { e.stopPropagation(); - if (this.indexInPres < (this.presBoxView?.itemIndex || 0)) { + if (this.presBox && this.indexInPres < (this.presBoxView?.itemIndex || 0)) { this.presBox.itemIndex = (this.presBoxView?.itemIndex || 0) - 1; } this.props.removeDocument?.(this.rootDoc); @@ -406,15 +403,15 @@ export class PresElementBox extends ViewBoxBaseComponent() { @computed get toolbarWidth(): number { const presBoxDocView = DocumentManager.Instance.getDocumentView(this.presBox); - let width: number = NumCast(this.presBox._width); + let width: number = NumCast(this.presBox?._width); if (presBoxDocView) width = presBoxDocView.props.PanelWidth(); if (width === 0) width = 300; return width; } @computed get presButtons() { - const presBox: Doc = this.presBox; //presBox - const presBoxColor: string = StrCast(presBox._backgroundColor); + const presBox = this.presBox; //presBox + const presBoxColor: string = StrCast(presBox?._backgroundColor); const presColorBool: boolean = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false; const targetDoc: Doc = this.targetDoc; const activeItem: Doc = this.rootDoc; @@ -494,10 +491,10 @@ export class PresElementBox extends ViewBoxBaseComponent() { @computed get mainItem() { const isSelected: boolean = this.selectedArray?.has(this.rootDoc) ? true : false; - const isCurrent: boolean = this.presBox._itemIndex === this.indexInPres; + const isCurrent: boolean = this.presBox?._itemIndex === this.indexInPres; const miniView: boolean = this.toolbarWidth <= 110; - const presBox: Doc = this.presBox; //presBox - const presBoxColor: string = StrCast(presBox._backgroundColor); + const presBox = this.presBox; //presBox + const presBoxColor: string = StrCast(presBox?._backgroundColor); const presColorBool: boolean = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false; const activeItem: Doc = this.rootDoc; diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index f2e9be61d..d63c25dbe 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -46,7 +46,7 @@ export class TopBar extends React.Component { return (
{Doc.ActiveDashboard ? ( - } isCircle={true} hoverStyle="gray" color={this.textColor} /> + } color={this.textColor} /> ) : (
dash logo @@ -55,18 +55,7 @@ export class TopBar extends React.Component {
)} {Doc.ActiveDashboard && ( -
); @@ -94,13 +83,8 @@ export class TopBar extends React.Component { -
+
{'('.repeat(parenCount)} {props.fieldKey} {')'.repeat(parenCount)} diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 3cfc3d696..9cf929679 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -870,7 +870,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent MarqueeAnnotator.clearAnnotations(this._savedAnnotations), - false + false, false ); } }; @@ -1026,13 +1026,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent (this._iframe = r))} src={url} onLoad={this.iframeLoaded} + scrolling="no" // ugh.. on windows, I get an inner scroll bar for the iframe's body even though the scrollHeight should be set to the full height of the document. // the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page // sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />; sandbox={`${this.layoutDoc.allowScripts ? 'allow-scripts' : ''} allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin`} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index e85835002..fb0c0d2ab 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -917,10 +917,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { + // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this) + if (this.props.isContentActive() && !this.props.allowScroll) { + if (!NumCast(this.layoutDoc._layout_scrollTop) && e.deltaY <= 0) e.preventDefault(); + e.stopPropagation(); + } + } + _oldWheel:any; render() { TraceMobx(); const active = this.props.isContentActive() || this.props.isSelected(); @@ -2019,18 +2027,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent (this._isHovering = true))} onPointerLeave={action(() => (this._isHovering = false))} - ref={r => - r?.addEventListener( - 'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this) - (e: WheelEvent) => { - if (this.props.isContentActive() && !this.props.allowScroll) { - if (!NumCast(this.layoutDoc._layout_scrollTop) && e.deltaY <= 0) e.preventDefault(); - e.stopPropagation(); - } - }, - { passive: false } - ) - } + ref={r => { + this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel); + this._oldWheel= r; + r?.addEventListener('wheel', this.onPassiveWheel, { passive: false } ); + }} style={{ ...(this.props.dontScale ? {} diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss index eb91c82f3..bf56b4d9e 100644 --- a/src/client/views/nodes/trails/PresBox.scss +++ b/src/client/views/nodes/trails/PresBox.scss @@ -951,6 +951,7 @@ margin-right: unset; height: 100%; position: relative; + user-select: none; } select { diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 14d463a6b..913018b69 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -196,7 +196,8 @@ export class PresBox extends ViewBoxBaseComponent() { this.turnOffEdit(true); this._disposers.selection = reaction( () => SelectionManager.Views(), - views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation() + views => (!PresBox.Instance || views.some(view => view.props.Document === this.rootDoc)) && this.updateCurrentPresentation(), + {fireImmediately:true} ); this._disposers.editing = reaction( () => this.layoutDoc.presStatus === PresStatus.Edit, @@ -832,7 +833,7 @@ export class PresBox extends ViewBoxBaseComponent() { } }); LightboxView.SetLightboxDoc(undefined); - Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc); + Doc.RemFromMyOverlay(this.rootDoc); return PresStatus.Edit; }; }; @@ -928,8 +929,8 @@ export class PresBox extends ViewBoxBaseComponent() { return PresBox.OpenPresMinimized(this.rootDoc, [pt[0] + (this.props.PanelWidth() - 250), pt[1] + 10]); }; exitMinimize = () => { - if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) { - Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc); + if (Doc.IsInMyOverlay(this.layoutDoc)) { + Doc.RemFromMyOverlay(this.rootDoc); CollectionDockingView.AddSplit(this.rootDoc, OpenWhereMod.right); } return PresStatus.Edit; @@ -941,7 +942,7 @@ export class PresBox extends ViewBoxBaseComponent() { doc.overlayY = pt[1]; doc._height = 30; doc._width = PresBox.minimizedWidth; - Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc); + Doc.AddToMyOverlay(doc); PresBox.Instance?.initializePresState(PresBox.Instance.itemIndex); return (doc.presStatus = PresStatus.Manual); } @@ -1158,7 +1159,7 @@ export class PresBox extends ViewBoxBaseComponent() { } break; case 'Escape': - if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) { + if (Doc.IsInMyOverlay(this.layoutDoc)) { this.exitClicked(); } else if (this.layoutDoc.presStatus === PresStatus.Edit) { this.clearSelectedArray(); @@ -2130,10 +2131,9 @@ export class PresBox extends ViewBoxBaseComponent() { const propTitle = SettingsManager.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel'; const mode = StrCast(this.rootDoc._type_collection) as CollectionViewType; const isMini: boolean = this.toolbarWidth <= 100; - const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc); const activeColor = Colors.LIGHT_BLUE; const inactiveColor = Colors.WHITE; - return mode === CollectionViewType.Carousel3D || inOverlay ? null : ( + return mode === CollectionViewType.Carousel3D || Doc.IsInMyOverlay(this.rootDoc) ? null : (
{/*
{"Add new slide"}
}>
this.newDocumentTools = !this.newDocumentTools)}> @@ -2174,9 +2174,8 @@ export class PresBox extends ViewBoxBaseComponent() { @computed get topPanel() { const mode = StrCast(this.rootDoc._type_collection) as CollectionViewType; const isMini: boolean = this.toolbarWidth <= 100; - const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc); return ( -
+
{isMini ? null : ( -
+
{'('.repeat(parenCount)} {props.fieldKey} {')'.repeat(parenCount)} diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 450176a49..079e1b56d 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -3,11 +3,12 @@ import { Tooltip } from '@material-ui/core'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import wiki from 'wikijs'; -import { Doc, DocCastAsync, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { Doc, DocCastAsync, Opt } from '../../../fields/Doc'; +import { Height, Width } from '../../../fields/DocSymbols'; import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils'; import { DocServer } from '../../DocServer'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; import { LinkFollower } from '../../util/LinkFollower'; @@ -183,17 +184,17 @@ export class LinkDocPreview extends React.Component { width = () => { if (!this._targetDoc) return 225; - if (this._targetDoc[WidthSym]() < this._targetDoc?.[HeightSym]()) { - return (Math.min(225, this._targetDoc[HeightSym]()) * this._targetDoc[WidthSym]()) / this._targetDoc[HeightSym](); + if (this._targetDoc[Width]() < this._targetDoc?.[Height]()) { + return (Math.min(225, this._targetDoc[Height]()) * this._targetDoc[Width]()) / this._targetDoc[Height](); } - return Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225)); + return Math.min(225, NumCast(this._targetDoc?.[Width](), 225)); }; height = () => { if (!this._targetDoc) return 225; - if (this._targetDoc[WidthSym]() > this._targetDoc?.[HeightSym]()) { - return (Math.min(225, this._targetDoc[WidthSym]()) * this._targetDoc[HeightSym]()) / this._targetDoc[WidthSym](); + if (this._targetDoc[Width]() > this._targetDoc?.[Height]()) { + return (Math.min(225, this._targetDoc[Width]()) * this._targetDoc[Height]()) / this._targetDoc[Width](); } - return Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225)); + return Math.min(225, NumCast(this._targetDoc?.[Height](), 225)); }; @computed get previewHeader() { return !this._linkDoc || !this._markerTargetDoc || !this._targetDoc || !this._linkSrc ? null : ( diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx index 9354f9639..18fcc951a 100644 --- a/src/client/views/nodes/MapBox/MapBox2.tsx +++ b/src/client/views/nodes/MapBox/MapBox2.tsx @@ -3,7 +3,8 @@ import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-m import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc'; +import { Doc, DocListCast, Opt } from '../../../../fields/Doc'; +import { Width } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { NumCast, StrCast } from '../../../../fields/Types'; @@ -367,7 +368,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent 0) { this._showSidebar = true; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 95fbb274d..6ee1b67f2 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -3,7 +3,8 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction import { observer } from 'mobx-react'; import * as Pdfjs from 'pdfjs-dist'; import 'pdfjs-dist/web/pdf_viewer.css'; -import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { Doc, DocListCast, Opt } from '../../../fields/Doc'; +import { Height, Width } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { ComputedField } from '../../../fields/ScriptField'; @@ -63,7 +64,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent (this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href))); else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action((pdf: any) => (this._pdf = pdf))); @@ -104,15 +105,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent { @@ -336,7 +337,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent= 1) { this.layoutDoc.nativeWidth = nativeWidth * ratio; - onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]); + onButton && (this.layoutDoc._width = this.layoutDoc[Width]() + localDelta[0]); this.layoutDoc._show_sidebar = nativeWidth !== this.layoutDoc._nativeWidth; } return false; @@ -357,11 +358,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent - {!this.result && } + {!this.result && }
); } diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 7bf765042..1e178b123 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -5,10 +5,11 @@ import { computed, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; // import { BufferAttribute, Camera, Vector2, Vector3 } from 'three'; import { DateField } from '../../../fields/DateField'; -import { Doc, HeightSym, WidthSym } from '../../../fields/Doc'; +import { Doc } from '../../../fields/Doc'; +import { Height, Width } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { ComputedField } from '../../../fields/ScriptField'; -import { Cast, NumCast } from '../../../fields/Types'; +import { Cast, DocCast, NumCast } from '../../../fields/Types'; import { AudioField, VideoField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils'; @@ -24,6 +25,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; import './ScreenshotBox.scss'; import { VideoBox } from './VideoBox'; + declare class MediaRecorder { constructor(e: any, options?: any); // whatever MediaRecorder has } @@ -140,7 +142,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent (NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], this.layoutDoc[WidthSym]())) * this.props.PanelWidth(); + videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], this.layoutDoc[Height]()) / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], this.layoutDoc[Width]())) * this.props.PanelWidth(); formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight()); render() { TraceMobx(); @@ -315,7 +317,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent { const makeIcon = (returnedfilename: string) => { this.dataDoc.icon = new ImageField(returnedfilename); - this.dataDoc.icon_nativeWidth = this.layoutDoc[WidthSym](); - this.dataDoc.icon_nativeHeight = this.layoutDoc[HeightSym](); + this.dataDoc.icon_nativeWidth = this.layoutDoc[Width](); + this.dataDoc.icon_nativeHeight = this.layoutDoc[Height](); }; this.Snapshot(undefined, undefined, makeIcon); }; @@ -465,7 +465,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent MarqueeAnnotator.clearAnnotations(this._savedAnnotations), - false, false + false, + false ); } }; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index a898398a1..48f77469f 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -2,16 +2,18 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as WebRequest from 'web-request'; -import { Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc'; +import { Height, Width } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { HtmlField } from '../../../fields/HtmlField'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; +import { RefField } from '../../../fields/RefField'; import { listSpec } from '../../../fields/Schema'; import { Cast, ImageCast, NumCast, StrCast, WebCast } from '../../../fields/Types'; import { ImageField, WebField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; -import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, getWordAtPoint, removeStyleSheetRule, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils'; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, getWordAtPoint, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; @@ -37,7 +39,6 @@ import { LinkDocPreview } from './LinkDocPreview'; import { PinProps, PresBox } from './trails'; import './WebBox.scss'; import React = require('react'); -import { RefField } from '../../../fields/RefField'; const { CreateImage } = require('./WebBoxRenderer'); const _global = (window /* browser */ || global) /* node */ as any; const htmlToText = require('html-to-text'); @@ -84,7 +85,7 @@ export class WebBox extends ViewBoxAnnotatableComponent 0.05) { if (!nativeWidth) Doc.SetNativeWidth(this.layoutDoc, 600); Doc.SetNativeHeight(this.layoutDoc, (nativeWidth || 600) / youtubeaspect); - this.layoutDoc._height = this.layoutDoc[WidthSym]() / youtubeaspect; + this.layoutDoc._height = this.layoutDoc[Width]() / youtubeaspect; } } // else it's an HTMLfield } else if (this.webField && !this.dataDoc.text) { @@ -281,7 +282,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { if (anchor !== this.rootDoc && this._outerRef.current) { const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); - const scrollTo = Utils.scrollIntoView(NumCast(anchor.y), anchor[HeightSym](), NumCast(this.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[HeightSym](), this._scrollHeight)); + const scrollTo = Utils.scrollIntoView(NumCast(anchor.y), anchor[Height](), NumCast(this.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[Height](), this._scrollHeight)); if (scrollTo !== undefined) { if (this._initialScroll === undefined) { const focusTime = options.zoomTime ?? 500; @@ -460,7 +461,7 @@ export class WebBox extends ViewBoxAnnotatableComponent= 1) { this.layoutDoc.nativeWidth = nativeWidth * ratio; this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio); - onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]); + onButton && (this.layoutDoc._width = this.layoutDoc[Width]() + localDelta[0]); this.layoutDoc._layout_showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; } return false; @@ -820,9 +821,9 @@ export class WebBox extends ViewBoxAnnotatableComponent { var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']); if (!nativeWidth) { - const defaultNativeWidth = this.rootDoc[this.fieldKey] instanceof WebField ? 850 : this.Document[WidthSym](); + const defaultNativeWidth = this.rootDoc[this.fieldKey] instanceof WebField ? 850 : this.Document[Width](); Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || defaultNativeWidth); - Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || (this.Document[HeightSym]() / this.Document[WidthSym]()) * defaultNativeWidth); + Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || (this.Document[Height]() / this.Document[Width]()) * defaultNativeWidth); nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']); } const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth; @@ -830,11 +831,11 @@ export class WebBox extends ViewBoxAnnotatableComponent { const x = NumCast(d.x); const y = NumCast(d.y); - const width = d[WidthSym](); - const height = d[HeightSym](); + const width = d[Width](); + const height = d[Height](); bounds.push({ x, y, width, height }); }) ); @@ -795,8 +796,8 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) { // TODO: nda - this is the code to actually get a new grouped collection const newCollection = marqViewRef?.getCollection(selected, undefined, true); if (newCollection) { - newCollection.height = newCollection[HeightSym](); - newCollection.width = newCollection[WidthSym](); + newCollection.height = newCollection[Height](); + newCollection.width = newCollection[Width](); } // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index c929b7ff3..1d41eb379 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -2,8 +2,9 @@ import { action, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { NodeSelection } from 'prosemirror-state'; import * as ReactDOM from 'react-dom/client'; -import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc'; -import { Cast, NumCast, StrCast } from '../../../../fields/Types'; +import { Doc } from '../../../../fields/Doc'; +import { Height, Width } from '../../../../fields/DocSymbols'; +import { NumCast, StrCast } from '../../../../fields/Types'; import { emptyFunction, returnFalse, Utils } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; import { Docs, DocUtils } from '../../../documents/Documents'; @@ -210,8 +211,8 @@ export class DashDocViewInternal extends React.Component { addDocTab={this._textBox.props.addDocTab} pinToPres={returnFalse} renderDepth={this._textBox.props.renderDepth + 1} - PanelWidth={this._finalLayout[WidthSym]} - PanelHeight={this._finalLayout[HeightSym]} + PanelWidth={this._finalLayout[Width]} + PanelHeight={this._finalLayout[Height]} focus={this.outerFocus} whenChildContentsActiveChanged={returnFalse} bringToFront={emptyFunction} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index fb0c0d2ab..bea789a5c 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -12,9 +12,11 @@ import { Fragment, Mark, Node, Slice } from 'prosemirror-model'; import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state'; import { EditorView } from 'prosemirror-view'; import { DateField } from '../../../../fields/DateField'; -import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, CssSym, Doc, DocListCast, Field, ForceServerWrite, HeightSym, Opt, StrListCast, UpdatingFromServer, WidthSym } from '../../../../fields/Doc'; +import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc'; +import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, ForceServerWrite, Height, UpdatingFromServer, Width } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; +import { List } from '../../../../fields/List'; import { PrefetchProxy } from '../../../../fields/Proxy'; import { RichTextField } from '../../../../fields/RichTextField'; import { RichTextUtils } from '../../../../fields/RichTextUtils'; @@ -34,6 +36,7 @@ import { DragManager } from '../../../util/DragManager'; import { MakeTemplate } from '../../../util/DropConverter'; import { IsFollowLinkScript } from '../../../util/LinkFollower'; import { LinkManager } from '../../../util/LinkManager'; +import { RTFMarkup } from '../../../util/RTFMarkup'; import { SelectionManager } from '../../../util/SelectionManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { undoBatch, UndoManager } from '../../../util/UndoManager'; @@ -68,8 +71,6 @@ import { schema } from './schema_rts'; import { SummaryView } from './SummaryView'; import applyDevTools = require('prosemirror-dev-tools'); import React = require('react'); -import { RTFMarkup } from '../../../util/RTFMarkup'; -import { List } from '../../../../fields/List'; const translateGoogleApi = require('translate-google-api'); export const GoogleRef = 'googleDocId'; type PullHandler = (exportState: Opt, dataDoc: Doc) => void; @@ -544,8 +545,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-hr-' + (hr - i), { opacity: ((10 - i - 1) / 10).toString() })); } - this.layoutDoc[CssSym] = this.layoutDoc[CssSym] + 1; // css changes happen outside of react/mobx. so we need to set a flag that will notify anyone intereted in layout changes triggered by css changes (eg., CollectionLinkView) + this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css changes happen outside of react/mobx. so we need to set a flag that will notify anyone intereted in layout changes triggered by css changes (eg., CollectionLinkView) }; @observable _showSidebar = false; @@ -682,7 +683,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc?.proto), this.fieldKey) ? DocCast(this.layoutDoc?.proto) : this?.dataDoc; const whichDoc = !this.dataDoc || !this.layoutDoc ? undefined : dataDoc?.[this.fieldKey + '_noTemplate'] || !this.layoutDoc[this.fieldKey] ? dataDoc : this.layoutDoc; - return !whichDoc ? undefined : { data: Cast(whichDoc[this.fieldKey], RichTextField, null), str: Field.toString(whichDoc[this.fieldKey]) }; + return !whichDoc ? undefined : { data: Cast(whichDoc[this.fieldKey], RichTextField, null), str: Field.toString(DocCast(whichDoc[this.fieldKey])) }; }, incomingValue => { if (this._editorView && this._applyingChange !== this.fieldKey) { @@ -1208,7 +1209,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this.props.isSelected(), action(selected => { if (FormattedTextBox._globalHighlights.has('Bold Text')) { - this.layoutDoc[CssSym] = this.layoutDoc[CssSym] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed + this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed } if (RichTextMenu.Instance?.view === this._editorView && !selected) { RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined); @@ -2000,14 +2001,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { + onPassiveWheel = (e: WheelEvent) => { // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this) if (this.props.isContentActive() && !this.props.allowScroll) { if (!NumCast(this.layoutDoc._layout_scrollTop) && e.deltaY <= 0) e.preventDefault(); e.stopPropagation(); } - } - _oldWheel:any; + }; + _oldWheel: any; render() { TraceMobx(); const active = this.props.isContentActive() || this.props.isSelected(); @@ -2029,8 +2030,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent (this._isHovering = false))} ref={r => { this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel); - this._oldWheel= r; - r?.addEventListener('wheel', this.onPassiveWheel, { passive: false } ); + this._oldWheel = r; + r?.addEventListener('wheel', this.onPassiveWheel, { passive: false }); }} style={{ ...(this.props.dontScale diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index f197a8a8d..154c879a0 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -2,12 +2,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../../fields/Doc'; +import { Doc, DocListCast, Opt } from '../../../../fields/Doc'; +import { Height, Width } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; import { Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils'; -import { Docs, DocUtils } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; import { CollectionViewType } from '../../../documents/DocumentTypes'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; @@ -322,7 +323,9 @@ export class PresElementBox extends ViewBoxBaseComponent() { }; removeAllRecordingInOverlay = () => { - DocListCast(Doc.MyOverlayDocs.data).filter(doc => doc.slides === this.rootDoc).forEach(Doc.RemFromMyOverlay); + DocListCast(Doc.MyOverlayDocs.data) + .filter(doc => doc.slides === this.rootDoc) + .forEach(Doc.RemFromMyOverlay); }; static removeEveryExistingRecordingInOverlay = () => { @@ -386,8 +389,8 @@ export class PresElementBox extends ViewBoxBaseComponent() { activeItem.recording = recording; // make recording box appear in the bottom right corner of the screen - recording.overlayX = window.innerWidth - recording[WidthSym]() - 20; - recording.overlayY = window.innerHeight - recording[HeightSym]() - 20; + recording.overlayX = window.innerWidth - recording[Width]() - 20; + recording.overlayY = window.innerHeight - recording[Height]() - 20; Doc.AddToMyOverlay(recording); } }; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 999e3eeb6..bc0cad636 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -2,7 +2,8 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio import { observer } from 'mobx-react'; import * as Pdfjs from 'pdfjs-dist'; import 'pdfjs-dist/web/pdf_viewer.css'; -import { Doc, DocListCast, Field, HeightSym, Opt } from '../../../fields/Doc'; +import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc'; +import { Height } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; @@ -181,7 +182,7 @@ export class PDFViewer extends React.Component { let focusSpeed: Opt; if (doc !== this.props.rootDoc && mainCont) { const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); - const scrollTo = Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight); + const scrollTo = Utils.scrollIntoView(scrollTop, doc[Height](), NumCast(this.props.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight); if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._layout_scrollTop) { if (!this._pdfViewer) this._initialScroll = { loc: scrollTo, easeFunc: options.easeFunc }; else if (!options.instant) this._scrollStopper = smoothScroll((focusSpeed = options.zoomTime ?? 500), mainCont, scrollTo, options.easeFunc, this._scrollStopper); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index d0e5d03b3..72eeedcff 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -11,9 +11,38 @@ import { SelectionManager } from '../client/util/SelectionManager'; import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from '../client/util/SerializationHelper'; import { undoable, UndoManager } from '../client/util/UndoManager'; import { decycle } from '../decycler/decycler'; +import * as JSZipUtils from '../JSZipUtils'; import { DashColor, incrementTitleCopy, intersectRect, Utils } from '../Utils'; import { DateField } from './DateField'; -import { Copy, HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from './FieldSymbols'; +import { + AclAdmin, + AclAugment, + AclEdit, + AclPrivate, + AclReadonly, + AclSelfEdit, + AclUnset, + Animation, + CachedUpdates, + DirectLinks, + DocAcl, + DocCss, + DocData, + DocFields, + DocLayout, + FieldKeys, + FieldTuples, + ForceServerWrite, + Height, + Highlight, + Initializing, + Self, + SelfProxy, + Update, + UpdatingFromServer, + Width, +} from './DocSymbols'; +import { Copy, HandleUpdate, Id, OnUpdate, Parent, ToScriptString, ToString } from './FieldSymbols'; import { InkField, InkTool } from './InkField'; import { List, ListFieldName } from './List'; import { ObjectField } from './ObjectField'; @@ -26,7 +55,6 @@ import { Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Ty import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from './URLField'; import { deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from './util'; import JSZip = require('jszip'); -import * as JSZipUtils from '../JSZipUtils'; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { const onDelegate = Object.keys(doc).includes(key.replace(/^_/, '')); @@ -93,28 +121,6 @@ export function DocListCast(field: FieldResult, defaultVal: Doc[] = []) { return Cast(field, listSpec(Doc), defaultVal).filter(d => d instanceof Doc) as Doc[]; } -export const WidthSym = Symbol('Width'); -export const HeightSym = Symbol('Height'); -export const AnimationSym = Symbol('Animation'); -export const HighlightSym = Symbol('Highlight'); -export const DataSym = Symbol('Data'); -export const LayoutSym = Symbol('Layout'); -export const FieldsSym = Symbol('Fields'); -export const CssSym = Symbol('Css'); -export const AclSym = Symbol('Acl'); -export const DirectLinksSym = Symbol('DirectLinks'); -export const AclUnset = Symbol('AclUnset'); -export const AclPrivate = Symbol('AclOwnerOnly'); -export const AclReadonly = Symbol('AclReadOnly'); -export const AclAugment = Symbol('AclAugment'); -export const AclSelfEdit = Symbol('AclSelfEdit'); -export const AclEdit = Symbol('AclEdit'); -export const AclAdmin = Symbol('AclAdmin'); -export const UpdatingFromServer = Symbol('UpdatingFromServer'); -export const Initializing = Symbol('Initializing'); -export const ForceServerWrite = Symbol('ForceServerWrite'); -export const CachedUpdates = Symbol('Cached updates'); - export enum aclLevel { unset = -1, unshared = 0, @@ -141,15 +147,15 @@ export const ReverseHierarchyMap: Map export function updateCachedAcls(doc: Doc) { if (!doc) return; - const target = (doc as any)?.__fields ?? doc; + const target = (doc as any)?.__fieldTuples ?? doc; const permissions: { [key: string]: symbol } = !target.author || target.author === Doc.CurrentUserEmail ? { 'acl-Me': AclAdmin } : {}; Object.keys(target).filter(key => key.startsWith('acl') && (permissions[key] = ReverseHierarchyMap.get(StrCast(target[key]))!.acl)); - if (Object.keys(permissions).length || doc[AclSym]?.length) { - runInAction(() => (doc[AclSym] = permissions)); + if (Object.keys(permissions).length || doc[DocAcl]?.length) { + runInAction(() => (doc[DocAcl] = permissions)); } if (doc.proto instanceof Promise) { - doc.proto.then(updateCachedAcls); + doc.proto.then(proto => updateCachedAcls(DocCast(proto))); return doc.proto; } } @@ -295,25 +301,20 @@ export class Doc extends RefField { set: setter, get: getter, // getPrototypeOf: (target) => Cast(target[SelfProxy].proto, Doc) || null, // TODO this might be able to replace the proto logic in getter - has: (target, key) => GetEffectiveAcl(target) !== AclPrivate && key in target.__fields, + has: (target, key) => GetEffectiveAcl(target) !== AclPrivate && key in target.__fieldTuples, ownKeys: target => { - const obj = {} as any; - if (GetEffectiveAcl(target) !== AclPrivate) Object.assign(obj, target.___fieldKeys); - runInAction(() => (obj.__LAYOUT__ = target.__LAYOUT__)); - return Object.keys(obj); + const keys = GetEffectiveAcl(target) !== AclPrivate ? Object.keys(target[FieldKeys]) : []; + return [...keys, '__LAYOUT__']; }, getOwnPropertyDescriptor: (target, prop) => { - if (prop.toString() === '__LAYOUT__') { + if (prop.toString() === '__LAYOUT__' || !(prop in target[FieldKeys])) { return Reflect.getOwnPropertyDescriptor(target, prop); } - if (prop in target.__fieldKeys) { - return { - configurable: true, //TODO Should configurable be true? - enumerable: true, - value: 0, //() => target.__fields[prop]) - }; - } - return Reflect.getOwnPropertyDescriptor(target, prop); + return { + configurable: true, //TODO Should configurable be true? + enumerable: true, + value: 0, //() => target.__fieldTuples[prop]) + }; }, deleteProperty: deleteProperty, defineProperty: () => { @@ -327,41 +328,35 @@ export class Doc extends RefField { return docProxy; } - proto: Opt; [key: string]: FieldResult; @serializable(alias('fields', map(autoObject(), { afterDeserialize: afterDocDeserialize }))) - private get __fields() { - return this.___fields; + private get __fieldTuples() { + return this[FieldTuples]; } - private set __fields(value) { - this.___fields = value; + private set __fieldTuples(value) { + // called by deserializer to set all fields in one shot + this[FieldTuples] = value; for (const key in value) { const field = value[key]; - field !== undefined && (this.__fieldKeys[key] = true); + field !== undefined && (this[FieldKeys][key] = true); if (!(field instanceof ObjectField)) continue; field[Parent] = this[Self]; field[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]); } } - private get __fieldKeys() { - return this.___fieldKeys; - } - private set __fieldKeys(value) { - this.___fieldKeys = value; - } - @observable private ___fields: any = {}; - @observable private ___fieldKeys: any = {}; + @observable private [FieldTuples]: any = {}; + @observable private [FieldKeys]: any = {}; /// all of the raw acl's that have been set on this document. Use GetEffectiveAcl to determine the actual ACL of the doc for editing - @observable public [AclSym]: { [key: string]: symbol } = {}; - @observable public [CssSym]: number = 0; // incrementer denoting a change to CSS layout - @observable public [DirectLinksSym]: Set = new Set(); - @observable public [AnimationSym]: Opt; - @observable public [HighlightSym]: boolean = false; + @observable public [DocAcl]: { [key: string]: symbol } = {}; + @observable public [DocCss]: number = 0; // incrementer denoting a change to CSS layout + @observable public [DirectLinks]: Set = new Set(); + @observable public [Animation]: Opt; + @observable public [Highlight]: boolean = false; static __Anim(Doc: Doc) { // for debugging to print AnimationSym field easily. - return Doc[AnimationSym]; + return Doc[Animation]; } private [UpdatingFromServer]: boolean = false; @@ -374,15 +369,15 @@ export class Doc extends RefField { private [Self] = this; private [SelfProxy]: any; - public [FieldsSym] = () => this[Self].___fields; // Object.keys(this).reduce((fields, key) => { fields[key] = this[key]; return fields; }, {} as any); - public [WidthSym] = () => NumCast(this[SelfProxy]._width); - public [HeightSym] = () => NumCast(this[SelfProxy]._height); + public [DocFields] = () => this[Self][FieldTuples]; // Object.keys(this).reduce((fields, key) => { fields[key] = this[key]; return fields; }, {} as any); + public [Width] = () => NumCast(this[SelfProxy]._width); + public [Height] = () => NumCast(this[SelfProxy]._height); public [ToScriptString] = () => `idToDoc("${this[Self][Id]}")`; public [ToString] = () => `Doc(${GetEffectiveAcl(this[SelfProxy]) === AclPrivate ? '-inaccessible-' : this[SelfProxy].title})`; - public get [LayoutSym]() { + public get [DocLayout]() { return this[SelfProxy].__LAYOUT__; } - public get [DataSym](): Doc { + public get [DocData](): Doc { const self = this[SelfProxy]; return self.resolvedDataDoc && !self.isTemplateForField ? self : Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self); } @@ -535,17 +530,17 @@ export namespace Doc { if (key.startsWith('_')) key = key.substring(1); const hasProto = Doc.GetProto(doc) !== doc ? Doc.GetProto(doc) : undefined; const onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1; - const onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1; + const onProto = hasProto && Object.getOwnPropertyNames(hasProto).indexOf(key) !== -1; if (onDeleg || !hasProto || (!onProto && !defaultProto)) { doc[key] = value; - } else doc.proto![key] = value; + } else hasProto[key] = value; } export function GetAllPrototypes(doc: Doc): Doc[] { const protos: Doc[] = []; let d: Opt = doc; while (d) { protos.push(d); - d = FieldValue(d.proto); + d = DocCast(FieldValue(d.proto)); } return protos; } @@ -585,7 +580,7 @@ export namespace Doc { // return the doc's proto, but rather recursively searches through the proto inheritance chain // and returns the document who's proto is undefined or whose proto is marked as a data doc ('isDataDoc'). export function GetProto(doc: Doc): Doc { - const proto = doc && (Doc.GetT(doc, 'isDataDoc', 'boolean', true) ? doc : doc.proto || doc); + const proto = doc && (Doc.GetT(doc, 'isDataDoc', 'boolean', true) ? doc : DocCast(doc.proto, doc)); return proto === doc ? proto : Doc.GetProto(proto); } export function GetDataDoc(doc: Doc): Doc { @@ -599,7 +594,7 @@ export namespace Doc { let proto: Doc | undefined = doc; while (proto) { Object.keys(proto).forEach(key => results.add(key)); - proto = proto.proto; + proto = DocCast(FieldValue(proto.proto)); } return Array.from(results); @@ -676,7 +671,7 @@ export namespace Doc { const bounds = docList.reduce( (bounds, doc) => { const [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)]; - const [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()]; + const [bptX, bptY] = [sptX + doc[Width](), sptY + doc[Height]()]; return { x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), @@ -700,7 +695,7 @@ export namespace Doc { embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`); embedding.author = Doc.CurrentUserEmail; - Doc.AddDocToList(Doc.GetProto(doc)[DataSym], 'proto_embeddings', embedding); + Doc.AddDocToList(Doc.GetProto(doc)[DocData], 'proto_embeddings', embedding); return embedding; } @@ -772,7 +767,7 @@ export namespace Doc { } }) ); - Array.from(doc[DirectLinksSym]).forEach(async link => { + Array.from(doc[DirectLinks]).forEach(async link => { if ( cloneLinks || ((cloneMap.has(DocCast(link.link_anchor_1)?.[Id]) || cloneMap.has(DocCast(DocCast(link.link_anchor_1)?.annotationOn)?.[Id])) && @@ -930,7 +925,7 @@ export namespace Doc { if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc))) { expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params } else { - templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = Cast(templateLayoutDoc.proto, Doc, null) || templateLayoutDoc); // if the template has already been applied (ie, a nested template), then use the template's prototype + templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = DocCast(templateLayoutDoc.proto, templateLayoutDoc)); // if the template has already been applied (ie, a nested template), then use the template's prototype if (!targetDoc[expandedLayoutFieldKey]) { _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey, true); setTimeout( @@ -969,7 +964,7 @@ export namespace Doc { const field = ProxyField.WithoutProxy(() => doc[key]); if (key === 'proto' && copyProto) { if (doc.proto instanceof Doc && overwrite.proto instanceof Doc) { - overwrite[key] = Doc.Overwrite(doc[key]!, overwrite.proto); + overwrite[key] = Doc.Overwrite(doc.proto, overwrite.proto); } } else { if (field instanceof RefField) { @@ -991,38 +986,37 @@ export namespace Doc { const copy = new Doc(copyProtoId, true); updateCachedAcls(copy); 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])); - const field = key === 'author' ? Doc.CurrentUserEmail : ProxyField.WithoutProxy(() => doc[key]); - if (key === 'proto' && copyProto) { - if (doc[key] instanceof Doc) { - copy[key] = Doc.MakeCopy(doc[key]!, false); - } - } else { - if (field instanceof RefField) { - copy[key] = field; - } else if (cfield instanceof ComputedField) { - copy[key] = cfield[Copy](); // ComputedField.MakeFunction(cfield.script.originalScript); - } else if (field instanceof ObjectField) { - copy[key] = - doc[key] instanceof Doc - ? key.includes('layout[') - ? undefined - : doc[key] // reference documents except remove documents that are expanded teplate fields - : ObjectField.MakeCopy(field); - } else if (field instanceof Promise) { - debugger; //This shouldn't happend... + Object.keys(doc) + .filter(key => !exclude.includes(key)) + .forEach(key => { + if (key === 'proto' && copyProto) { + if (doc.proto instanceof Doc) { + copy[key] = Doc.MakeCopy(doc.proto, false); + } } else { - copy[key] = field; + const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); + const field = key === 'author' ? Doc.CurrentUserEmail : ProxyField.WithoutProxy(() => doc[key]); + if (field instanceof RefField) { + copy[key] = field; + } else if (cfield instanceof ComputedField) { + copy[key] = cfield[Copy](); // ComputedField.MakeFunction(cfield.script.originalScript); + } else if (field instanceof ObjectField) { + copy[key] = + doc[key] instanceof Doc && key.includes('layout[') + ? undefined // remove expanded template field documents + : ObjectField.MakeCopy(field); + } else if (field instanceof Promise) { + debugger; //This shouldn't happend... + } else { + copy[key] = field; + } } - } - }); + }); if (copyProto) { Doc.GetProto(copy).embedContainer = undefined; Doc.GetProto(copy).proto_embeddings = new List([copy]); } else { - Doc.AddDocToList(Doc.GetProto(copy)[DataSym], 'proto_embeddings', copy); + Doc.AddDocToList(Doc.GetProto(copy)[DocData], 'proto_embeddings', copy); } copy.embedContainer = undefined; Doc.defaultAclPrivate && (copy['acl-Public'] = 'Not Shared'); @@ -1045,7 +1039,7 @@ export namespace Doc { Object.keys(doc) .filter(key => key.startsWith('acl')) .forEach(key => (delegate[key] = doc[key])); - if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DataSym], 'proto_embeddings', delegate); + if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DocData], 'proto_embeddings', delegate); title && (delegate.title = title); delegate[Initializing] = false; Doc.AddFileOrphan(delegate); @@ -1069,7 +1063,7 @@ export namespace Doc { delegate[Initializing] = true; delegate.proto = delegateProto; delegate.author = Doc.CurrentUserEmail; - Doc.AddDocToList(delegateProto[DataSym], 'proto_embeddings', delegate); + Doc.AddDocToList(delegateProto[DocData], 'proto_embeddings', delegate); delegate[Initializing] = false; delegateProto[Initializing] = false; return delegate; @@ -1173,7 +1167,7 @@ export namespace Doc { } export const brushManager = new DocBrush(); - export class DocData { + export class UserDocData { @observable _user_doc: Doc = undefined!; @observable _sharing_doc: Doc = undefined!; @observable _searchQuery: string = ''; @@ -1183,7 +1177,7 @@ export namespace Doc { // a layout field or 'layout' is given. export function Layout(doc: Doc, layout?: Doc): Doc { const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, 'data')}-layout[` + layout[Id] + ']'], Doc, null); - return overrideLayout || doc[LayoutSym] || doc; + return overrideLayout || doc[DocLayout] || doc; } export function SetLayout(doc: Doc, layout: Doc | string) { doc[StrCast(doc.layout_fieldKey, 'layout')] = layout; @@ -1198,12 +1192,12 @@ export namespace Doc { return Doc.NativeWidth(doc, dataDoc, useDim) / (Doc.NativeHeight(doc, dataDoc, useDim) || 1); } export function NativeWidth(doc?: Doc, dataDoc?: Doc, useWidth?: boolean) { - return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeWidth'], useWidth ? doc[WidthSym]() : 0)); + return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeWidth'], useWidth ? doc[Width]() : 0)); } export function NativeHeight(doc?: Doc, dataDoc?: Doc, useHeight?: boolean) { if (!doc) return 0; - const nheight = (Doc.NativeWidth(doc, dataDoc, useHeight) * doc[HeightSym]()) / doc[WidthSym](); - const dheight = NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeHeight'], useHeight ? doc[HeightSym]() : 0); + const nheight = (Doc.NativeWidth(doc, dataDoc, useHeight) * doc[Height]()) / doc[Width](); + const dheight = NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeHeight'], useHeight ? doc[Height]() : 0); return NumCast(doc._nativeHeight, nheight || dheight); } export function SetNativeWidth(doc: Doc, width: number | undefined, fieldKey?: string) { @@ -1213,7 +1207,7 @@ export namespace Doc { doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '_nativeHeight'] = height; } - const manager = new DocData(); + const manager = new UserDocData(); export function SearchQuery(): string { return manager._searchQuery; } @@ -1348,16 +1342,16 @@ export namespace Doc { export var highlightedDocs = new ObservableSet(); export function IsHighlighted(doc: Doc) { if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return false; - return doc[HighlightSym] || Doc.GetProto(doc)[HighlightSym]; + return doc[Highlight] || Doc.GetProto(doc)[Highlight]; } export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true, presEffect?: Doc) { runInAction(() => { highlightedDocs.add(doc); - doc[HighlightSym] = true; - doc[AnimationSym] = presEffect; + doc[Highlight] = true; + doc[Animation] = presEffect; if (dataAndDisplayDocs) { highlightedDocs.add(Doc.GetProto(doc)); - Doc.GetProto(doc)[HighlightSym] = true; + Doc.GetProto(doc)[Highlight] = true; } }); } @@ -1367,8 +1361,8 @@ export namespace Doc { (doc ? [doc] : Array.from(highlightedDocs)).forEach(doc => { highlightedDocs.delete(doc); highlightedDocs.delete(Doc.GetProto(doc)); - doc[HighlightSym] = Doc.GetProto(doc)[HighlightSym] = false; - doc[AnimationSym] = undefined; + doc[Highlight] = Doc.GetProto(doc)[Highlight] = false; + doc[Animation] = undefined; }); }); } diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts new file mode 100644 index 000000000..65decc147 --- /dev/null +++ b/src/fields/DocSymbols.ts @@ -0,0 +1,26 @@ +export const Update = Symbol('DocUpdate'); +export const Self = Symbol('DocSelf'); +export const SelfProxy = Symbol('DocSelfProxy'); +export const FieldKeys = Symbol('DocFieldKeys'); +export const FieldTuples = Symbol('DocFieldTuples'); +export const Width = Symbol('DocWidth'); +export const Height = Symbol('DocHeight'); +export const Animation = Symbol('DocAnimation'); +export const Highlight = Symbol('DocHighlight'); +export const DocData = Symbol('DocData'); +export const DocLayout = Symbol('DocLayout'); +export const DocFields = Symbol('DocFields'); +export const DocCss = Symbol('DocCss'); +export const DocAcl = Symbol('DocAcl'); +export const DirectLinks = Symbol('DocDirectLinks'); +export const AclUnset = Symbol('DocAclUnset'); +export const AclPrivate = Symbol('DocAclOwnerOnly'); +export const AclReadonly = Symbol('DocAclReadOnly'); +export const AclAugment = Symbol('DocAclAugment'); +export const AclSelfEdit = Symbol('DocAclSelfEdit'); +export const AclEdit = Symbol('DocAclEdit'); +export const AclAdmin = Symbol('DocAclAdmin'); +export const UpdatingFromServer = Symbol('DocUpdatingFromServer'); +export const Initializing = Symbol('DocInitializing'); +export const ForceServerWrite = Symbol('DocForceServerWrite'); +export const CachedUpdates = Symbol('DocCachedUpdates'); diff --git a/src/fields/FieldSymbols.ts b/src/fields/FieldSymbols.ts index e50c2856f..c381f14f5 100644 --- a/src/fields/FieldSymbols.ts +++ b/src/fields/FieldSymbols.ts @@ -1,12 +1,9 @@ -export const Update = Symbol('Update'); -export const Self = Symbol('Self'); -export const SelfProxy = Symbol('SelfProxy'); -export const HandleUpdate = Symbol('HandleUpdate'); -export const Id = Symbol('Id'); -export const OnUpdate = Symbol('OnUpdate'); -export const Parent = Symbol('Parent'); -export const Copy = Symbol('Copy'); -export const ToValue = Symbol('ToValue'); -export const ToScriptString = Symbol('ToScriptString'); -export const ToPlainText = Symbol('ToPlainText'); -export const ToString = Symbol('ToString'); +export const HandleUpdate = Symbol('FieldHandleUpdate'); +export const Id = Symbol('FieldId'); +export const OnUpdate = Symbol('FieldOnUpdate'); +export const Parent = Symbol('FieldParent'); +export const Copy = Symbol('FieldCopy'); +export const ToValue = Symbol('FieldToValue'); +export const ToScriptString = Symbol('FieldToScriptString'); +export const ToPlainText = Symbol('FieldToPlainText'); +export const ToString = Symbol('FieldToString'); diff --git a/src/fields/List.ts b/src/fields/List.ts index dfd24cf7a..033fa569b 100644 --- a/src/fields/List.ts +++ b/src/fields/List.ts @@ -3,8 +3,9 @@ import { alias, list, serializable } from 'serializr'; import { DocServer } from '../client/DocServer'; import { ScriptingGlobals } from '../client/util/ScriptingGlobals'; import { afterDocDeserialize, autoObject, Deserializable } from '../client/util/SerializationHelper'; +import { FieldTuples, Self, SelfProxy, Update } from './DocSymbols'; import { Field } from './Doc'; -import { Copy, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from './FieldSymbols'; +import { Copy, OnUpdate, Parent, ToScriptString, ToString } from './FieldSymbols'; import { ObjectField } from './ObjectField'; import { ProxyField } from './Proxy'; import { RefField } from './RefField'; @@ -21,12 +22,12 @@ const listHandlers: any = { if (value instanceof RefField) { throw new Error('fill with RefFields not supported yet'); } - const res = this[Self].__fields.fill(value, start, end); + const res = this[Self].__fieldTuples.fill(value, start, end); this[Update](); return res; }, pop(): any { - const field = toRealField(this[Self].__fields.pop()); + const field = toRealField(this[Self].__fieldTuples.pop()); this[Update](); return field; }, @@ -34,7 +35,7 @@ const listHandlers: any = { items = items.map(toObjectField); const list = this[Self]; - const length = list.__fields.length; + const length = list.__fieldTuples.length; for (let i = 0; i < items.length; i++) { const item = items[i]; //TODO Error checking to make sure parent doesn't already exist @@ -43,23 +44,23 @@ const listHandlers: any = { item[OnUpdate] = updateFunction(list, i + length, item, this); } } - const res = list.__fields.push(...items); + const res = list.__fieldTuples.push(...items); this[Update]({ op: '$addToSet', items, length: length + items.length }); return res; }), reverse() { - const res = this[Self].__fields.reverse(); + const res = this[Self].__fieldTuples.reverse(); this[Update](); return res; }, shift() { - const res = toRealField(this[Self].__fields.shift()); + const res = toRealField(this[Self].__fieldTuples.shift()); this[Update](); return res; }, sort(cmpFunc: any) { this[Self].__realFields(); // coerce retrieving entire array - const res = this[Self].__fields.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined); + const res = this[Self].__fieldTuples.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined); this[Update](); return res; }, @@ -67,7 +68,7 @@ const listHandlers: any = { this[Self].__realFields(); // coerce retrieving entire array items = items.map(toObjectField); const list = this[Self]; - const removed = list.__fields.filter((item: any, i: number) => i >= start && i < start + deleteCount); + const removed = list.__fieldTuples.filter((item: any, i: number) => i >= start && i < start + deleteCount); for (let i = 0; i < items.length; i++) { const item = items[i]; //TODO Error checking to make sure parent doesn't already exist @@ -77,18 +78,18 @@ const listHandlers: any = { item[OnUpdate] = updateFunction(list, i + start, item, this); } } - let hintArray: {val : any, index : number}[] = []; - for(let i = start; i < start + deleteCount; i++) { - hintArray.push({val : list.__fields[i], index : i}); + let hintArray: { val: any; index: number }[] = []; + for (let i = start; i < start + deleteCount; i++) { + hintArray.push({ val: list.__fieldTuples[i], index: i }); } - const res = list.__fields.splice(start, deleteCount, ...items); + const res = list.__fieldTuples.splice(start, deleteCount, ...items); // the hint object sends the starting index of the slice and the number - // of elements to delete. + // of elements to delete. this[Update]( items.length === 0 && deleteCount - ? { op: '$remFromSet', items: removed, hint : { start : start, deleteCount : deleteCount }, length: list.__fields.length } - : items.length && !deleteCount && start === list.__fields.length - ? { op: '$addToSet', items, length: list.__fields.length } + ? { op: '$remFromSet', items: removed, hint: { start: start, deleteCount: deleteCount }, length: list.__fieldTuples.length } + : items.length && !deleteCount && start === list.__fieldTuples.length + ? { op: '$addToSet', items, length: list.__fieldTuples.length } : undefined ); return res.map(toRealField); @@ -96,7 +97,7 @@ const listHandlers: any = { unshift(...items: any[]) { items = items.map(toObjectField); const list = this[Self]; - const length = list.__fields.length; + const length = list.__fieldTuples.length; for (let i = 0; i < items.length; i++) { const item = items[i]; //TODO Error checking to make sure parent doesn't already exist @@ -106,32 +107,32 @@ const listHandlers: any = { item[OnUpdate] = updateFunction(list, i, item, this); } } - const res = this[Self].__fields.unshift(...items); + const res = this[Self].__fieldTuples.unshift(...items); this[Update](); return res; }, /// Accessor methods concat: action(function (this: any, ...items: any[]) { this[Self].__realFields(); - return this[Self].__fields.map(toRealField).concat(...items); + return this[Self].__fieldTuples.map(toRealField).concat(...items); }), includes(valueToFind: any, fromIndex: number) { if (valueToFind instanceof RefField) { return this[Self].__realFields().includes(valueToFind, fromIndex); } else { - return this[Self].__fields.includes(valueToFind, fromIndex); + return this[Self].__fieldTuples.includes(valueToFind, fromIndex); } }, indexOf(valueToFind: any, fromIndex: number) { if (valueToFind instanceof RefField) { return this[Self].__realFields().indexOf(valueToFind, fromIndex); } else { - return this[Self].__fields.indexOf(valueToFind, fromIndex); + return this[Self].__fieldTuples.indexOf(valueToFind, fromIndex); } }, join(separator: any) { this[Self].__realFields(); - return this[Self].__fields.map(toRealField).join(separator); + return this[Self].__fieldTuples.map(toRealField).join(separator); }, lastElement() { return this[Self].__realFields().lastElement(); @@ -140,12 +141,12 @@ const listHandlers: any = { if (valueToFind instanceof RefField) { return this[Self].__realFields().lastIndexOf(valueToFind, fromIndex); } else { - return this[Self].__fields.lastIndexOf(valueToFind, fromIndex); + return this[Self].__fieldTuples.lastIndexOf(valueToFind, fromIndex); } }, slice(begin: number, end: number) { this[Self].__realFields(); - return this[Self].__fields.slice(begin, end).map(toRealField); + return this[Self].__fieldTuples.slice(begin, end).map(toRealField); }, /// Iteration methods @@ -156,55 +157,55 @@ const listHandlers: any = { return this[Self].__realFields().every(callback, thisArg); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); + // return this[Self].__fieldTuples.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); }, filter(callback: any, thisArg: any) { return this[Self].__realFields().filter(callback, thisArg); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); + // return this[Self].__fieldTuples.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); }, find(callback: any, thisArg: any) { return this[Self].__realFields().find(callback, thisArg); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); + // return this[Self].__fieldTuples.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); }, findIndex(callback: any, thisArg: any) { return this[Self].__realFields().findIndex(callback, thisArg); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); + // return this[Self].__fieldTuples.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); }, forEach(callback: any, thisArg: any) { return this[Self].__realFields().forEach(callback, thisArg); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); + // return this[Self].__fieldTuples.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); }, map(callback: any, thisArg: any) { return this[Self].__realFields().map(callback, thisArg); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); + // return this[Self].__fieldTuples.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); }, reduce(callback: any, initialValue: any) { return this[Self].__realFields().reduce(callback, initialValue); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue); + // return this[Self].__fieldTuples.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue); }, reduceRight(callback: any, initialValue: any) { return this[Self].__realFields().reduceRight(callback, initialValue); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue); + // return this[Self].__fieldTuples.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue); }, some(callback: any, thisArg: any) { return this[Self].__realFields().some(callback, thisArg); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); + // return this[Self].__fieldTuples.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); }, values() { return this[Self].__realFields().values(); @@ -246,7 +247,7 @@ type ListUpdate = ListSpliceUpdate | ListIndexUpdate; type StoredType = T extends RefField ? ProxyField : T; -export const ListFieldName="fields"; +export const ListFieldName = 'fields'; @Deserializable('list') class ListImpl extends ObjectField { constructor(fields?: T[]) { @@ -254,9 +255,9 @@ class ListImpl extends ObjectField { const list = new Proxy(this, { set: setter, get: listGetter, - ownKeys: target => Object.keys(target.__fields), + ownKeys: target => Object.keys(target.__fieldTuples), getOwnPropertyDescriptor: (target, prop) => { - if (prop in target.__fields) { + if (prop in target[FieldTuples]) { return { configurable: true, //TODO Should configurable be true? enumerable: true, @@ -281,7 +282,7 @@ class ListImpl extends ObjectField { // this requests all ProxyFields at the same time to avoid the overhead // of separate network requests and separate updates to the React dom. private __realFields() { - const unrequested = this.__fields.filter(f => f instanceof ProxyField && f.needsRequesting).map(f => f as ProxyField); + const unrequested = this[FieldTuples].filter(f => f instanceof ProxyField && f.needsRequesting).map(f => f as ProxyField); // if we find any ProxyFields that don't have a current value, then // start the server request for all of them if (unrequested.length) { @@ -293,17 +294,16 @@ class ListImpl extends ObjectField { // will await the batch request and return the requested field value. unrequested.forEach(p => p.setExternalValuePromise(allSetPromise)); } - return this.__fields.map(toRealField); + return this[FieldTuples].map(toRealField); } - public static FieldDataName = 'fields'; @serializable(alias(ListFieldName, list(autoObject(), { afterDeserialize: afterDocDeserialize }))) - private get __fields() { - return this.___fields; + private get __fieldTuples() { + return this[FieldTuples]; } - private set __fields(value) { - this.___fields = value; + private set __fieldTuples(value) { + this[FieldTuples] = value; for (const key in value) { const field = value[key]; if (field instanceof ObjectField) { @@ -314,14 +314,14 @@ class ListImpl extends ObjectField { } [Copy]() { - const copiedData = this[Self].__fields.map(f => (f instanceof ObjectField ? f[Copy]() : f)); + const copiedData = this[Self].__fieldTuples.map(f => (f instanceof ObjectField ? f[Copy]() : f)); const deepCopy = new ListImpl(copiedData as any); return deepCopy; } // @serializable(alias("fields", list(autoObject()))) @observable - private ___fields: StoredType[] = []; + private [FieldTuples]: StoredType[] = []; private [Update] = (diff: any) => { // console.log(diff); diff --git a/src/fields/util.ts b/src/fields/util.ts index a2b445d6c..62f27c7d2 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -7,28 +7,9 @@ import { SerializationHelper } from '../client/util/SerializationHelper'; import { UndoManager } from '../client/util/UndoManager'; import { returnZero } from '../Utils'; import CursorField from './CursorField'; -import { - AclAdmin, - AclEdit, - aclLevel, - AclPrivate, - AclSelfEdit, - AclSym, - DataSym, - Doc, - DocListCast, - DocListCastAsync, - ForceServerWrite, - HeightSym, - HierarchyMapping, - Initializing, - LayoutSym, - ReverseHierarchyMap, - updateCachedAcls, - UpdatingFromServer, - WidthSym, -} from './Doc'; -import { Id, OnUpdate, Parent, SelfProxy, ToValue, Update } from './FieldSymbols'; +import { aclLevel, Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap, updateCachedAcls } from './Doc'; +import { AclAdmin, AclEdit, AclPrivate, AclSelfEdit, DocAcl, DocData, DocLayout, FieldKeys, ForceServerWrite, Height, Initializing, SelfProxy, Update, UpdatingFromServer, Width } from './DocSymbols'; +import { Id, OnUpdate, Parent, ToValue } from './FieldSymbols'; import { List } from './List'; import { ObjectField } from './ObjectField'; import { PrefetchProxy, ProxyField } from './Proxy'; @@ -56,7 +37,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number if (value !== undefined) { value = value[SelfProxy] || value; } - const curValue = target.__fields[prop]; + const curValue = target.__fieldTuples[prop]; if (curValue === value || (curValue instanceof ProxyField && value instanceof RefField && curValue.fieldId === value[Id])) { // TODO This kind of checks correctly in the case that curValue is a ProxyField and value is a RefField, but technically // curValue should get filled in with value if it isn't already filled in, in case we fetched the referenced field some other way @@ -88,11 +69,11 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number if (writeToDoc) { if (value === undefined) { - target.__fieldKeys && delete target.__fieldKeys[prop]; - delete target.__fields[prop]; + delete target[FieldKeys][prop]; + delete target.__fieldTuples[prop]; } else { - target.__fieldKeys && (target.__fieldKeys[prop] = true); - target.__fields[prop] = value; + target[FieldKeys][prop] = true; + target.__fieldTuples[prop] = value; } if (writeToServer) { @@ -148,12 +129,12 @@ export function denormalizeEmail(email: string) { */ export function inheritParentAcls(parent: Doc, child: Doc) { return; - const dataDoc = parent[DataSym]; - for (const key of Object.keys(dataDoc)) { - // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private. - const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key]; - key.startsWith('acl') && distributeAcls(key, permission, child); - } + // const dataDoc = parent[DataSym]; + // for (const key of Object.keys(dataDoc)) { + // // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private. + // const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key]; + // key.startsWith('acl') && distributeAcls(key, permission, child); + // } } /** @@ -212,7 +193,7 @@ export function SetCachedGroups(groups: string[]) { runInAction(() => cachedGroups.push(...groups)); } function getEffectiveAcl(target: any, user?: string): symbol { - const targetAcls = target[AclSym]; + const targetAcls = target[DocAcl]; if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin; const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group @@ -237,7 +218,7 @@ function getEffectiveAcl(target: any, user?: string): symbol { return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl; } // authored documents are private until an ACL is set. - const targetAuthor = target.__fields?.author || target.author; // target may be a Doc of Proxy, so check __fields.author and .author + const targetAuthor = target.__fieldTuples?.author || target.author; // target may be a Doc of Proxy, so check __fieldTuples.author and .author if (targetAuthor && targetAuthor !== userChecked) return AclPrivate; return AclAdmin; } @@ -277,7 +258,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc } let dataDocChanged = false; - const dataDoc = target[DataSym]; + const dataDoc = target[DocData]; if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || ReverseHierarchyMap.get(StrCast(dataDoc[key]))! > ReverseHierarchyMap.get(acl)!)) { if (GetEffectiveAcl(dataDoc) === AclAdmin) { dataDoc[key] = acl; @@ -290,13 +271,13 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc // maps over the children of the document DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => { distributeAcls(key, acl, d, inheritingFromCollection, visited); - distributeAcls(key, acl, d[DataSym], inheritingFromCollection, visited); + distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited); }); // maps over the annotations of the document DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '_annotations']).forEach(d => { distributeAcls(key, acl, d, inheritingFromCollection, visited); - distributeAcls(key, acl, d[DataSym], inheritingFromCollection, visited); + distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited); }); } @@ -311,16 +292,16 @@ export function setter(target: any, in_prop: string | symbol | number, value: an // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't if (typeof prop === 'string' && prop.startsWith('acl') && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value))) return true; - if (typeof prop === 'string' && prop !== '__id' && prop !== '__fields' && prop.startsWith('_')) { + if (typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('_')) { if (!prop.startsWith('__')) prop = prop.substring(1); if (target.__LAYOUT__) { target.__LAYOUT__[prop] = value; return true; } } - if (target.__fields[prop] instanceof ComputedField) { - if (target.__fields[prop].setterscript && value !== undefined && !(value instanceof ComputedField)) { - return ScriptCast(target.__fields[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false; + if (target.__fieldTuples[prop] instanceof ComputedField) { + if (target.__fieldTuples[prop].setterscript && value !== undefined && !(value instanceof ComputedField)) { + return ScriptCast(target.__fieldTuples[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false; } } return _setter(target, prop, value, receiver); @@ -330,14 +311,13 @@ export function getter(target: any, prop: string | symbol, proxy: any): any { // prettier-ignore switch (prop) { case 'then' : return undefined; - case '__fields' : case '__id': - case 'constructor': case 'toString': case 'valueOf': - case 'factory': case 'serializeInfo': + case 'constructor': case 'toString': case 'valueOf': + case 'serializeInfo': case 'factory': return target[prop]; - case AclSym : return target[AclSym]; - case $mobx: return target.__fields[prop]; - case LayoutSym: return target.__LAYOUT__; - case HeightSym: case WidthSym: if (GetEffectiveAcl(target) === AclPrivate) return returnZero; + case DocAcl : return target[DocAcl]; + case $mobx: return target.__fieldTuples[prop]; + case DocLayout: return target.__LAYOUT__; + case Height: case Width: if (GetEffectiveAcl(target) === AclPrivate) return returnZero; default : if (typeof prop === 'symbol') return target[prop]; if (prop.startsWith('isMobX')) return target[prop]; @@ -351,7 +331,7 @@ export function getter(target: any, prop: string | symbol, proxy: any): any { } function getFieldImpl(target: any, prop: string | number, proxy: any, ignoreProto: boolean = false): any { - const field = target.__fields[prop]; + const field = target.__fieldTuples[prop]; const value = field?.[ToValue]?.(proxy); // converts ComputedFields to values, or unpacks ProxyFields into Proxys if (value) return value.value; if (field === undefined && !ignoreProto && prop !== 'proto') { -- 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/OverlayView.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 = (