From 871925ecc9f15377339afc907a2000fecfe3240d Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 7 Mar 2023 17:46:40 -0500 Subject: fixed presbox following to use presItem anchor instead of target doc as anchor. stopped allowing collections to have their data fields interpolate based on keyframe. fixed assigning current frame when following presbox --- src/client/util/DocumentManager.ts | 2 +- src/client/util/LinkFollower.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index c670d0ab8..f2c554866 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -286,7 +286,7 @@ export class DocumentManager { docView.rootDoc.layoutKey === 'layout_icon' ? await new Promise(res => docView.iconify(res)) : undefined; docView.props.focus(docView.rootDoc, options); // focus the view within its container const { childDocView, viewSpec } = await iterator(docView); - if (!childDocView) return { viewSpec: viewSpec ?? docView.rootDoc, docView, contextView }; + if (!childDocView) return { viewSpec: options.anchorDoc ?? viewSpec ?? docView.rootDoc, docView, contextView }; contextView = docView; docView = childDocView; } diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index c9a178db7..e28687561 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -76,7 +76,6 @@ export class LinkFollower { easeFunc: StrCast(sourceDoc.followLinkEase, 'ease') as any, openLocation: StrCast(sourceDoc.followLinkLocation, OpenWhere.lightbox), effect: sourceDoc, - originatingDoc: sourceDoc, zoomTextSelections: BoolCast(sourceDoc.followLinkZoomText), }; if (target.type === DocumentType.PRES) { -- cgit v1.2.3-70-g09d2 From 096371e683b3f91edfadb2aaf9f8da3309b86014 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 9 Mar 2023 21:39:21 -0500 Subject: added reordering of progressivized docs --- src/client/documents/Documents.ts | 1 + src/client/util/CurrentUserUtils.ts | 2 +- .../views/collections/CollectionTreeView.tsx | 4 +-- src/client/views/collections/CollectionView.tsx | 4 +-- src/client/views/collections/TreeView.tsx | 12 ++++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/trails/PresBox.tsx | 41 ++++++++++++---------- 7 files changed, 37 insertions(+), 29 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a7378c431..6296b0df4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -335,6 +335,7 @@ export class DocumentOptions { strokeWidth?: 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 '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. diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index a7b2c3d04..2b0a2cdac 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -269,7 +269,7 @@ export class CurrentUserUtils { {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, _isLinkButton: true }}, {key: "Script", creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }}, // {key: "DataViz", creator: opts => Docs.Create.DataVizDocument(opts), opts: { _width: 300, _height: 300 }}, - {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true,}}, + {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, treeViewHideUnrendered: true}}, {key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _viewType: CollectionViewType.Stacking, targetDropAction: "alias" as any, treeViewHideTitle: true, _fitWidth:true, _chromeHidden: true, boxShadow: "0 0" }}, {key: "Tab", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 500, _height: 800, _fitWidth: true, _backgroundGridShow: true, }}, {key: "Slide", creator: opts => Docs.Create.TreeDocument([], opts), opts: { _width: 300, _height: 200, _viewType: CollectionViewType.Tree, diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 330e116d1..553967b95 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -38,8 +38,8 @@ export type collectionTreeViewProps = { onCheckedClick?: () => ScriptField; onChildClick?: () => ScriptField; // TODO: [AL] add these fields - AddToMap?: (treeViewDoc: Doc, index: number[]) => Doc[]; - RemFromMap?: (treeViewDoc: Doc, index: number[]) => Doc[]; + AddToMap?: (treeViewDoc: Doc, index: number[]) => void; + RemFromMap?: (treeViewDoc: Doc, index: number[]) => void; hierarchyIndex?: number[]; }; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 316f1e4e9..48e5748a0 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -64,8 +64,8 @@ interface CollectionViewProps_ extends FieldViewProps { childClickScript?: ScriptField; childDoubleClickScript?: ScriptField; //TODO: [AL] add these fields - AddToMap?: (treeViewDoc: Doc, index: number[]) => Doc[]; - RemFromMap?: (treeViewDoc: Doc, index: number[]) => Doc[]; + AddToMap?: (treeViewDoc: Doc, index: number[]) => void; + RemFromMap?: (treeViewDoc: Doc, index: number[]) => void; hierarchyIndex?: number[]; // hierarchical index of a document up to the rendering root (primarily used for tree views) } export interface CollectionViewProps extends React.PropsWithChildren {} diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 0d2968ba1..c8dc68eae 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -66,8 +66,8 @@ export interface TreeViewProps { skipFields?: string[]; firstLevel: boolean; // TODO: [AL] add these - AddToMap?: (treeViewDoc: Doc, index: number[]) => Doc[]; - RemFromMap?: (treeViewDoc: Doc, index: number[]) => Doc[]; + AddToMap?: (treeViewDoc: Doc, index: number[]) => void; + RemFromMap?: (treeViewDoc: Doc, index: number[]) => void; hierarchyIndex?: number[]; } @@ -1028,7 +1028,9 @@ export class TreeView extends React.Component { // renders the text version of a document as the header. This is used in the file system mode and in other vanilla tree views. @computed get renderTitleAsHeader() { - return ( + return this.props.treeView.Document.treeViewHideUnrendered && this.doc.unrendered && !this.doc.treeViewFieldKey ? ( +
+ ) : ( <> {this.renderBullet} {this.renderTitle} @@ -1146,8 +1148,8 @@ export class TreeView extends React.Component { unobserveHeight: (ref: any) => void, contextMenuItems: { script: ScriptField; filter: ScriptField; label: string; icon: string }[], // TODO: [AL] add these - AddToMap?: (treeViewDoc: Doc, index: number[]) => Doc[], - RemFromMap?: (treeViewDoc: Doc, index: number[]) => Doc[], + AddToMap?: (treeViewDoc: Doc, index: number[]) => void, + RemFromMap?: (treeViewDoc: Doc, index: number[]) => void, hierarchyIndex?: number[], renderCount?: number ) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b92a8ec4d..87177cd74 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -519,7 +519,7 @@ export class CollectionFreeFormView extends CollectionSubView() { this.activeItem.presIndexed = presIndexed + 1; } return true; - } else { - console.log(this.activeItem.presIndexed); } } }; @@ -359,8 +358,6 @@ export class PresBox extends ViewBoxBaseComponent() { Doc.UnBrushAllDocs(); if (index >= 0 && index < this.childDocs.length) { this.rootDoc._itemIndex = index; - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; if (from?.mediaStopTriggerList && this.layoutDoc.presStatus !== PresStatus.Edit) { DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia); } @@ -368,8 +365,8 @@ export class PresBox extends ViewBoxBaseComponent() { this.stopTempMedia(from.presentationTargetDoc); } // If next slide is audio / video 'Play automatically' then the next slide should be played - if (this.layoutDoc.presStatus !== PresStatus.Edit && (targetDoc.type === DocumentType.AUDIO || targetDoc.type === DocumentType.VID) && activeItem.mediaStart === 'auto') { - this.startTempMedia(targetDoc, activeItem); + if (this.layoutDoc.presStatus !== PresStatus.Edit && (this.targetDoc.type === DocumentType.AUDIO || this.targetDoc.type === DocumentType.VID) && this.activeItem.mediaStart === 'auto') { + this.startTempMedia(this.targetDoc, this.activeItem); } if (!group) this.clearSelectedArray(); this.childDocs[index] && this.addToSelectedArray(this.childDocs[index]); //Update selected array @@ -755,17 +752,18 @@ export class PresBox extends ViewBoxBaseComponent() { const curDoc = Cast(doc, Doc, null); const tagDoc = PresBox.targetRenderedDoc(curDoc); const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc); + let opacity: Opt = index === this.itemIndex ? 1 : undefined; if (curDoc.presHide) { if (index !== this.itemIndex) { - tagDoc.opacity = 1; + opacity = 1; } } const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex) ?? itemIndexes.slice().reverse().lastElement(); if (curDoc.presHideBefore && index === hidingIndBef) { if (index > this.itemIndex) { - tagDoc.opacity = 0; + opacity = 0; } else if (index === this.itemIndex || !curDoc.presHideAfter) { - tagDoc.opacity = 1; + opacity = 1; setTimeout(() => (tagDoc._dataTransition = undefined), 1000); } } @@ -776,17 +774,18 @@ export class PresBox extends ViewBoxBaseComponent() { .find(item => item <= this.itemIndex) ?? itemIndexes.lastElement(); if (curDoc.presHideAfter && index === hidingIndAft) { if (index < this.itemIndex) { - tagDoc.opacity = 0; + opacity = 0; } else if (index === this.itemIndex || !curDoc.presHideBefore) { - tagDoc.opacity = 1; + opacity = 1; } } const hidingInd = itemIndexes.find(item => item === this.itemIndex); if (curDoc.presHide && index === hidingInd) { if (index === this.itemIndex) { - tagDoc.opacity = 0; + opacity = 0; } } + opacity !== undefined && (tagDoc.opacity = opacity); }); }; @@ -882,7 +881,6 @@ export class PresBox extends ViewBoxBaseComponent() { if (doc.presHideBefore && index > startIndex) tagDoc.opacity = 0; if (doc.presHideAfter && index < startIndex) tagDoc.opacity = 0; if (doc.presIndexed !== undefined && index >= startIndex) { - const type = DocCast(tagDoc.annotationOn)?.type ?? tagDoc.type; const startInd = NumCast(doc.presIndexedStart); this.progressivizedItems(doc) ?.slice(startInd) @@ -1552,6 +1550,13 @@ export class PresBox extends ViewBoxBaseComponent() { const tagDoc = PresBox.targetRenderedDoc(this.activeItem); const type = DocCast(tagDoc?.annotationOn)?.type ?? tagDoc.type; activeItem.presIndexedStart = type === DocumentType.COL ? 1 : 0; + // a progressivized slide doesn't have sub-slides, but rather iterates over the data list of the target being progressivized. + // to avoid creating a new slide to correspond to each of the target's data list, we simply reference the target's data list. + let dataField = Doc.LayoutFieldKey(tagDoc); + if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField = dataField + '-annotations'; + + if (DocCast(activeItem.presentationTargetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`self.presentationTargetDoc.annotationOn["${dataField}"]`); + else activeItem.data = ComputedField.MakeFunction(`self.presentationTargetDoc["${dataField}"]`); }} checked={Cast(activeItem.presIndexed, 'number', null) !== undefined ? true : false} /> @@ -2364,7 +2369,8 @@ export class PresBox extends ViewBoxBaseComponent() { clearTimeout(this._presTimer); }; - AddToMap = (treeViewDoc: Doc, index: number[]): Doc[] => { + AddToMap = (treeViewDoc: Doc, index: number[]) => { + if (!treeViewDoc.presentationTargetDoc) return this.childDocs; // if treeViewDoc is not a pres elements, then it's a sub-bullet of a progressivized slide which isn't added to the linearized list of pres elements since it's not really a pres element. var indexNum = 0; for (let i = 0; i < index.length; i++) { indexNum += index[i] * 10 ** -i; @@ -2377,15 +2383,14 @@ export class PresBox extends ViewBoxBaseComponent() { this.dataDoc[this.presFieldKey] = new List(sorted); // this is a flat array of Docs } } - return this.childDocs; }; - RemFromMap = (treeViewDoc: Doc, index: number[]): Doc[] => { + RemFromMap = (treeViewDoc: Doc, index: number[]) => { + if (!treeViewDoc.presentationTargetDoc) return this.childDocs; // if treeViewDoc is not a pres elements, then it's a sub-bullet of a progressivized slide which isn't added to the linearized list of pres elements since it's not really a pres element. if (!this._unmounting && this.isTree) { this._treeViewMap.delete(treeViewDoc); this.dataDoc[this.presFieldKey] = new List(this.sort(this._treeViewMap)); } - return this.childDocs; }; sort = (treeViewMap: Map) => [...treeViewMap.entries()].sort((a: [Doc, number], b: [Doc, number]) => (a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0)).map(kv => kv[0]); -- cgit v1.2.3-70-g09d2 From b599afe5fdcf8afc38432e34b1e175f82499d25b Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 11 Mar 2023 14:05:18 -0500 Subject: changed links from being a computed field on documents to being accessed by a function call on LinkManager --- src/client/documents/Documents.ts | 101 ++++++--------------- src/client/util/CaptureManager.tsx | 23 +++-- src/client/util/CurrentUserUtils.ts | 3 +- src/client/util/LinkFollower.ts | 3 +- src/client/util/LinkManager.ts | 17 +++- src/client/util/SelectionManager.ts | 4 +- src/client/views/LightboxView.tsx | 9 +- src/client/views/PropertiesButtons.tsx | 10 +- src/client/views/PropertiesView.tsx | 8 +- src/client/views/SidebarAnnos.tsx | 7 +- src/client/views/StyleProvider.tsx | 11 ++- .../collections/CollectionStackedTimeline.tsx | 7 +- src/client/views/collections/CollectionSubView.tsx | 1 - src/client/views/collections/TreeView.tsx | 3 +- src/client/views/linking/LinkMenu.tsx | 1 - src/client/views/linking/LinkMenuItem.tsx | 6 +- src/client/views/nodes/AudioBox.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 6 +- src/client/views/nodes/LinkDocPreview.tsx | 9 +- src/client/views/nodes/VideoBox.tsx | 5 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 18 ++-- src/client/views/nodes/trails/PresBox.tsx | 3 +- src/client/views/search/SearchBox.tsx | 3 +- src/fields/util.ts | 23 ++--- 24 files changed, 121 insertions(+), 163 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 4dc1c3b71..251e432ef 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { action, runInAction } from 'mobx'; import { basename } from 'path'; import { DateField } from '../../fields/DateField'; -import { Doc, DocListCast, DocListCastAsync, Field, Initializing, Opt, updateCachedAcls } from '../../fields/Doc'; +import { Doc, DocListCast, Field, Initializing, Opt, updateCachedAcls } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { HtmlField } from '../../fields/HtmlField'; import { InkField, PointData } from '../../fields/InkField'; @@ -403,7 +403,6 @@ export namespace Docs { nativeDimModifiable: true, nativeHeightUnfrozen: true, forceReflow: true, - links: '@links(self)', }, }, ], @@ -411,35 +410,35 @@ export namespace Docs { DocumentType.SEARCH, { layout: { view: SearchBox, dataField: defaultDataKey }, - options: { _width: 400, links: '@links(self)' }, + options: { _width: 400 }, }, ], [ DocumentType.COLOR, { layout: { view: ColorBox, dataField: defaultDataKey }, - options: { _nativeWidth: 220, _nativeHeight: 300, links: '@links(self)' }, + options: { _nativeWidth: 220, _nativeHeight: 300 }, }, ], [ DocumentType.IMG, { layout: { view: ImageBox, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ DocumentType.WEB, { layout: { view: WebBox, dataField: defaultDataKey }, - options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: '@links(self)' }, + options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true }, }, ], [ DocumentType.COL, { layout: { view: CollectionView, dataField: defaultDataKey }, - options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: '@links(self)' }, + options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1 }, }, ], [ @@ -453,35 +452,35 @@ export namespace Docs { DocumentType.VID, { layout: { view: VideoBox, dataField: defaultDataKey }, - options: { _currentTimecode: 0, links: '@links(self)' }, + options: { _currentTimecode: 0 }, }, ], [ DocumentType.AUDIO, { layout: { view: AudioBox, dataField: defaultDataKey }, - options: { _height: 100, backgroundColor: 'lightGray', _fitWidth: true, forceReflow: true, nativeDimModifiable: true, links: '@links(self)' }, + options: { _height: 100, forceReflow: true, nativeDimModifiable: true }, }, ], [ DocumentType.REC, { layout: { view: VideoBox, dataField: defaultDataKey }, - options: { _height: 100, backgroundColor: 'pink', links: '@links(self)' }, + options: { _height: 100, backgroundColor: 'pink' }, }, ], [ DocumentType.PDF, { layout: { view: PDFBox, dataField: defaultDataKey }, - options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: '@links(self)' }, + options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true }, }, ], [ DocumentType.MAP, { layout: { view: MapBox, dataField: defaultDataKey }, - options: { _height: 600, _width: 800, nativeDimModifiable: true, links: '@links(self)' }, + options: { _height: 600, _width: 800, nativeDimModifiable: true }, }, ], [ @@ -502,7 +501,6 @@ export namespace Docs { description: '', showCaption: 'description', backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area - links: '@links(self)', _removeDropProperties: new List(['isLinkButton']), }, }, @@ -527,7 +525,7 @@ export namespace Docs { DocumentType.SCRIPTING, { layout: { view: ScriptingBox, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ @@ -540,56 +538,56 @@ export namespace Docs { DocumentType.LABEL, { layout: { view: LabelBox, dataField: defaultDataKey }, - options: { links: '@links(self)', _singleLine: true }, + options: { _singleLine: true }, }, ], [ DocumentType.EQUATION, { layout: { view: EquationBox, dataField: defaultDataKey }, - options: { links: '@links(self)', nativeDimModifiable: true, fontSize: '14px', hideResizeHandles: true, hideDecorationTitle: true }, + options: { nativeDimModifiable: true, fontSize: '14px', hideResizeHandles: true, hideDecorationTitle: true }, }, ], [ DocumentType.FUNCPLOT, { layout: { view: FunctionPlotBox, dataField: defaultDataKey }, - options: { nativeDimModifiable: true, links: '@links(self)' }, + options: { nativeDimModifiable: true }, }, ], [ DocumentType.BUTTON, { layout: { view: LabelBox, dataField: 'onClick' }, - options: { links: '@links(self)' }, + options: {}, }, ], [ DocumentType.SLIDER, { layout: { view: SliderBox, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ DocumentType.PRES, { layout: { view: PresBox, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ DocumentType.FONTICON, { layout: { view: FontIconBox, dataField: defaultDataKey }, - options: { allowClickBeforeDoubleClick: true, hideLinkButton: true, _width: 40, _height: 40, borderRounding: '100%', links: '@links(self)' }, + options: { allowClickBeforeDoubleClick: true, hideLinkButton: true, _width: 40, _height: 40, borderRounding: '100%' }, }, ], [ DocumentType.WEBCAM, { layout: { view: RecordingBox, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ @@ -603,7 +601,7 @@ export namespace Docs { DocumentType.MARKER, { layout: { view: CollectionView, dataField: defaultDataKey }, - options: { links: '@links(self)', hideLinkButton: true, pointerEvents: 'none' }, + options: { hideLinkButton: true, pointerEvents: 'none' }, }, ], [ @@ -611,21 +609,21 @@ export namespace Docs { { // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method layout: { view: InkingStroke, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ DocumentType.SCREENSHOT, { layout: { view: ScreenshotBox, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ DocumentType.COMPARISON, { layout: { view: ComparisonBox, dataField: defaultDataKey }, - options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: 'gray', targetDropAction: 'alias', links: '@links(self)' }, + options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: 'gray', targetDropAction: 'alias' }, }, ], [ @@ -640,21 +638,21 @@ export namespace Docs { DocumentType.GROUP, { layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ DocumentType.DATAVIZ, { layout: { view: DataVizBox, dataField: defaultDataKey }, - options: { _fitWidth: true, nativeDimModifiable: true, links: '@links(self)' }, + options: { _fitWidth: true, nativeDimModifiable: true }, }, ], [ DocumentType.LOADING, { layout: { view: LoadingBox, dataField: '' }, - options: { _fitWidth: true, _fitHeight: true, nativeDimModifiable: true, links: '@links(self)' }, + options: { _fitWidth: true, _fitHeight: true, nativeDimModifiable: true }, }, ], ]); @@ -881,15 +879,7 @@ export namespace Docs { } export function AudioDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) { - return InstanceFromProto( - Prototypes.get(DocumentType.AUDIO), - new AudioField(url), - { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any }, - undefined, - undefined, - undefined, - overwriteDoc - ); + return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url), options, undefined, undefined, undefined, overwriteDoc); } export function RecordingDocument(url: string, options: DocumentOptions = {}) { @@ -992,7 +982,6 @@ export namespace Docs { I.creationDate = new DateField(); I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; //I['acl-Override'] = SharingPermissions.Unset; - I.links = ComputedField.MakeFunction('links(self)'); I[Initializing] = false; return I; } @@ -1282,38 +1271,6 @@ export namespace DocUtils { return rangeFilteredDocs; } - export function Publish(promoteDoc: Doc, targetID: string, addDoc: any, remDoc: any) { - targetID = targetID.replace(/^-/, '').replace(/\([0-9]*\)$/, ''); - DocServer.GetRefField(targetID).then(doc => { - if (promoteDoc !== doc) { - let copy = doc as Doc; - if (copy) { - Doc.Overwrite(promoteDoc, copy, true); - } else { - copy = Doc.MakeCopy(promoteDoc, true, targetID); - } - !doc && (copy.title = undefined) && (Doc.GetProto(copy).title = targetID); - addDoc && addDoc(copy); - remDoc && remDoc(promoteDoc); - if (!doc) { - DocListCastAsync(promoteDoc.links).then(links => { - links && - links.map(async link => { - if (link) { - const a1 = await Cast(link.anchor1, Doc); - if (a1 && Doc.AreProtosEqual(a1, promoteDoc)) link.anchor1 = copy; - const a2 = await Cast(link.anchor2, Doc); - if (a2 && Doc.AreProtosEqual(a2, promoteDoc)) link.anchor2 = copy; - LinkManager.Instance.deleteLink(link); - LinkManager.Instance.addLink(link); - } - }); - }); - } - } - }); - } - export function DefaultFocus(doc: Doc, options: DocFocusOptions) { return undefined; } @@ -1711,7 +1668,7 @@ export namespace DocUtils { export function LeavePushpin(doc: Doc, annotationField: string) { if (doc.followLinkToggle) return undefined; const context = Cast(doc.context, Doc, null) ?? Cast(doc.annotationOn, Doc, null); - const hasContextAnchor = DocListCast(doc.links).some(l => (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) || (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context)); + const hasContextAnchor = LinkManager.Links(doc).some(l => (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) || (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context)); 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', diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx index 735b06f6d..c9fcc84a3 100644 --- a/src/client/util/CaptureManager.tsx +++ b/src/client/util/CaptureManager.tsx @@ -2,12 +2,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, DocListCast } from '../../fields/Doc'; -import { StrCast } from '../../fields/Types'; +import { Doc } from '../../fields/Doc'; +import { DocCast, StrCast } from '../../fields/Types'; import { addStyleSheet } from '../../Utils'; import { LightboxView } from '../views/LightboxView'; import { MainViewModal } from '../views/MainViewModal'; import './CaptureManager.scss'; +import { LinkManager } from './LinkManager'; import { SelectionManager } from './SelectionManager'; @observer @@ -48,16 +49,14 @@ export class CaptureManager extends React.Component<{}> { const doc = this._document; const order: JSX.Element[] = []; if (doc) { - DocListCast(doc.links).forEach((l, i) => { - if (l) { - order.push( -
-
{i}
- {StrCast((l.anchor1 as Doc).title)} -
- ); - } - }); + LinkManager.Links(doc).forEach((l, i) => + order.push( +
+
{i}
+ {StrCast(DocCast(l.anchor1)?.title)} +
+ ) + ); } return ( diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 2b0a2cdac..c4fb4788c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -22,7 +22,7 @@ import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; import { OverlayView } from "../views/OverlayView"; -import { DragManager, dropActionType } from "./DragManager"; +import { DragManager } from "./DragManager"; import { MakeTemplate } from "./DropConverter"; import { LinkManager } from "./LinkManager"; import { ScriptingGlobals } from "./ScriptingGlobals"; @@ -949,6 +949,5 @@ ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Do ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, "creates a new presentation when called"); ScriptingGlobals.add(function openPresentation(pres:Doc) { return MainView.Instance.openPresentation(pres); }, "creates a new presentation when called"); ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called"); -ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)"); ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); \ No newline at end of file diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index e28687561..eacbcc0e3 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -6,6 +6,7 @@ import { DocumentDecorations } from '../views/DocumentDecorations'; import { DocFocusOptions, DocumentViewSharedProps, OpenWhere } from '../views/nodes/DocumentView'; import { PresBox } from '../views/nodes/trails'; import { DocumentManager } from './DocumentManager'; +import { LinkManager } from './LinkManager'; import { SelectionManager } from './SelectionManager'; import { UndoManager } from './UndoManager'; @@ -41,7 +42,7 @@ export class LinkFollower { }; public static traverseLink(link: Opt, sourceDoc: Doc, finished?: () => void, traverseBacklink?: boolean) { - const linkDocs = link ? [link] : DocListCast(sourceDoc.links); + const linkDocs = link ? [link] : LinkManager.Links(sourceDoc); const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor1 const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor2 const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews((d.anchor2 as Doc).type === DocumentType.MARKER ? DocCast((d.anchor2 as Doc).annotationOn) : (d.anchor2 as Doc)).length === 0); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 46d44fea4..555215417 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -4,6 +4,7 @@ import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from ' import { List } from '../../fields/List'; import { ProxyField } from '../../fields/Proxy'; import { Cast, DocCast, PromiseValue, StrCast } from '../../fields/Types'; +import { ScriptingGlobals } from './ScriptingGlobals'; /* * link doc: * - anchor1: doc @@ -24,6 +25,10 @@ export class LinkManager { public static get Instance() { return LinkManager._instance; } + + public static Links(doc: Doc | undefined) { + return doc ? LinkManager.Instance.getAllRelatedLinks(doc) : []; + } public static addLinkDB = async (linkDb: any) => { await Promise.all( ((await DocListCastAsync(linkDb.data)) ?? []).map(link => @@ -34,7 +39,7 @@ export class LinkManager { LinkManager.userLinkDBs.push(linkDb); }; public static AutoKeywords = 'keywords:Usages'; - static links: Doc[] = []; + static _links: Doc[] = []; constructor() { LinkManager._instance = this; this.createLinkrelationshipLists(); @@ -72,7 +77,7 @@ export class LinkManager { ); }; const watchUserLinkDB = (userLinkDBDoc: Doc) => { - LinkManager.links.push(...DocListCast(userLinkDBDoc.data)); + LinkManager._links.push(...DocListCast(userLinkDBDoc.data)); const toRealField = (field: Field) => (field instanceof ProxyField ? field.value : field); // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields if (userLinkDBDoc.data) { observe( @@ -193,3 +198,11 @@ export class LinkManager { if (Doc.AreProtosEqual(anchor, linkDoc)) return linkDoc; } } + +ScriptingGlobals.add( + function links(doc: any) { + return new List(LinkManager.Links(doc)); + }, + 'returns all the links to the document or its annotations', + '(doc: any)' +); diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 646942569..c0fc25376 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,6 +1,6 @@ import { action, observable, ObservableMap } from 'mobx'; import { computedFn } from 'mobx-utils'; -import { Doc, DocListCast, Opt } from '../../fields/Doc'; +import { Doc, Opt } from '../../fields/Doc'; import { DocCast } from '../../fields/Types'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { DocumentView } from '../views/nodes/DocumentView'; @@ -22,7 +22,7 @@ export namespace SelectionManager { // if doc is not in SelectedDocuments, add it if (!manager.SelectedViews.get(docView) && docView.props.Document.type !== DocumentType.MARKER) { if (!ctrlPressed) { - if (LinkManager.currentLink && !DocListCast(docView.rootDoc.links).includes(LinkManager.currentLink) && docView.rootDoc !== LinkManager.currentLink) { + if (LinkManager.currentLink && !LinkManager.Links(docView.rootDoc).includes(LinkManager.currentLink) && docView.rootDoc !== LinkManager.currentLink) { LinkManager.currentLink = undefined; } this.DeselectAll(); diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 2567d44bb..976c8763e 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -4,9 +4,8 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { InkTool } from '../../fields/InkField'; -import { List } from '../../fields/List'; -import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils'; +import { Cast, NumCast, StrCast } from '../../fields/Types'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../Utils'; import { DocUtils } from '../documents/Documents'; import { DocumentManager } from '../util/DocumentManager'; import { LinkManager } from '../util/LinkManager'; @@ -88,7 +87,7 @@ export class LightboxView extends React.Component { ...future .slice() .sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)) - .sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length), + .sort((a, b) => LinkManager.Links(a).length - LinkManager.Links(b).length), ]; } this._doc = doc; @@ -214,7 +213,7 @@ export class LightboxView extends React.Component { if (coll) { const fieldKey = Doc.LayoutFieldKey(coll); const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '-annotations'])]; - const links = DocListCast(coll.links) + const links = LinkManager.Links(coll) .map(link => LinkManager.getOppositeAnchor(link, coll)) .filter(doc => doc) .map(doc => doc!); diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index ac1b66013..ebbe20077 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -9,8 +9,10 @@ import { RichTextField } from '../../fields/RichTextField'; import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, ScriptCast, StrCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; +import { Utils } from '../../Utils'; import { DocUtils } from '../documents/Documents'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; +import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; import { undoBatch } from '../util/UndoManager'; import { Colors } from './global/globalEnums'; @@ -19,8 +21,6 @@ import { DocumentView, OpenWhere } from './nodes/DocumentView'; import { pasteImageBitmap } from './nodes/WebBoxRenderer'; import './PropertiesButtons.scss'; import React = require('react'); -import { LinkManager } from '../util/LinkManager'; -import { Utils } from '../../Utils'; const higflyout = require('@hig/flyout'); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -139,9 +139,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { 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 => { - DocListCast(doc.links).forEach(link => (link.linkDisplay = false)); - }); + containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.linkDisplay = false))); }); } ); @@ -330,7 +328,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { const click = () => this.handleOptionChange(value[0]); const linkButton = BoolCast(this.selectedDoc._isLinkButton); const followLoc = this.selectedDoc._followLinkLocation; - const linkedToLightboxView = () => DocListCast(this.selectedDoc.links).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); + const linkedToLightboxView = () => LinkManager.Links(this.selectedDoc).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); let active = false; // prettier-ignore diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 7a985628f..03b4100a7 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -4,10 +4,10 @@ import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-soli import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Checkbox, Tooltip } from '@material-ui/core'; import { intersection } from 'lodash'; -import { action, autorun, computed, Lambda, observable } from 'mobx'; +import { action, computed, Lambda, observable } from 'mobx'; import { observer } from 'mobx-react'; import { ColorState, SketchPicker } from 'react-color'; -import { AclAdmin, AclSym, HierarchyMapping, DataSym, Doc, DocListCast, Field, HeightSym, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc'; +import { AclAdmin, AclSym, DataSym, Doc, Field, HeightSym, HierarchyMapping, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; import { List } from '../../fields/List'; @@ -23,10 +23,10 @@ import { SharingManager } from '../util/SharingManager'; import { Transform } from '../util/Transform'; import { undoBatch, UndoManager } from '../util/UndoManager'; import { EditableView } from './EditableView'; +import { FilterPanel } from './FilterPanel'; import { Colors } from './global/globalEnums'; import { InkStrokeProperties } from './InkStrokeProperties'; import { DocumentView, OpenWhere, StyleProviderFunc } from './nodes/DocumentView'; -import { FilterPanel } from './FilterPanel'; import { KeyValueBox } from './nodes/KeyValueBox'; import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails'; import { PropertiesButtons } from './PropertiesButtons'; @@ -1486,7 +1486,7 @@ export class PropertiesView extends React.Component { const zoom = Number((NumCast(this.sourceAnchor?.followLinkZoomScale, 1) * 100).toPrecision(3)); const targZoom = this.sourceAnchor?.followLinkZoom; const indent = 30; - const hasSelectedAnchor = SelectionManager.Views().some(dv => DocListCast(this.sourceAnchor?.links).includes(LinkManager.currentLink!)); + const hasSelectedAnchor = LinkManager.Links(this.sourceAnchor).includes(LinkManager.currentLink!); if (!this.selectedDoc && !this.isPres) { return (
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 74ea624a6..7519cbb05 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -3,10 +3,12 @@ import { observer } from 'mobx-react'; import { Doc, DocListCast, Field, FieldResult, StrListCast } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { List } from '../../fields/List'; +import { RichTextField } from '../../fields/RichTextField'; import { DocCast, NumCast, StrCast } from '../../fields/Types'; import { emptyFunction, OmitKeys, returnAll, returnOne, returnTrue, returnZero } from '../../Utils'; import { Docs, DocUtils } from '../documents/Documents'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; +import { LinkManager } from '../util/LinkManager'; import { Transform } from '../util/Transform'; import { CollectionStackingView } from './collections/CollectionStackingView'; import { FieldViewProps } from './nodes/FieldView'; @@ -15,7 +17,6 @@ import { SearchBox } from './search/SearchBox'; import './SidebarAnnos.scss'; import { StyleProp } from './StyleProvider'; import React = require('react'); -import { RichTextField } from '../../fields/RichTextField'; interface ExtraProps { fieldKey: string; @@ -174,8 +175,8 @@ export class SidebarAnnos extends React.Component { showTitle = () => 'title'; setHeightCallback = (height: number) => this.props.setHeight?.(height + this.filtersHeight()); sortByLinkAnchorY = (a: Doc, b: Doc) => { - const ay = DocListCast(a.links).length && DocCast(DocListCast(a.links)[0].anchor1).y; - const by = DocListCast(b.links).length && DocCast(DocListCast(b.links)[0].anchor1).y; + const ay = LinkManager.Links(a).length && DocCast(LinkManager.Links(a)[0].anchor1).y; + const by = LinkManager.Links(b).length && DocCast(LinkManager.Links(b)[0].anchor1).y; return NumCast(ay) - NumCast(by); }; render() { diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 153c30052..ce764c7bf 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -1,12 +1,15 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Shadows } from 'browndash-components'; import { action, runInAction } from 'mobx'; import { extname } from 'path'; -import { Doc, DocListCast, Opt } from '../../fields/Doc'; -import { BoolCast, Cast, DocCast, ImageCast, NumCast, StrCast } from '../../fields/Types'; +import { Doc, Opt } from '../../fields/Doc'; +import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../fields/Types'; import { DashColor, emptyFunction, lightOrDark } from '../../Utils'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { DocFocusOrOpen } from '../util/DocumentManager'; +import { LinkManager } from '../util/LinkManager'; +import { SelectionManager } from '../util/SelectionManager'; import { ColorScheme } from '../util/SettingsManager'; import { undoBatch, UndoManager } from '../util/UndoManager'; import { TreeSort } from './collections/TreeView'; @@ -18,8 +21,6 @@ import { FieldViewProps } from './nodes/FieldView'; import { SliderBox } from './nodes/SliderBox'; import './StyleProvider.scss'; import React = require('react'); -import { Shadows } from 'browndash-components'; -import { SelectionManager } from '../util/SelectionManager'; export enum StyleProp { TreeViewIcon = 'treeViewIcon', @@ -261,7 +262,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt () => this.props.currentTimecode(), time => { const dictationDoc = Cast(this.props.layoutDoc['data-dictation'], Doc, null); - const isDictation = dictationDoc && DocListCast(this.props.mark.links).some(link => Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc); + const isDictation = dictationDoc && LinkManager.Links(this.props.mark).some(link => Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc); if ( !LightboxView.LightboxDoc && // bcz: when should links be followed? we don't want to move away from the video to follow a link but we can open it in a sidebar/etc. But we don't know that upfront. // for now, we won't follow any links when the lightbox is oepn to avoid "losing" the video. /*(isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this.props.layoutDoc))*/ !this.props.layoutDoc.dontAutoFollowLinks && - DocListCast(this.props.mark.links).length && + LinkManager.Links(this.props.mark).length && time > NumCast(this.props.mark[this.props.startTag]) && time < NumCast(this.props.mark[this.props.endTag]) && this._lastTimecode < NumCast(this.props.mark[this.props.startTag]) - 1e-5 diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index c88ae314e..e46220f02 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -1,5 +1,4 @@ import { action, computed, observable } from 'mobx'; -import ReactLoading from 'react-loading'; import * as rp from 'request-promise'; import CursorField from '../../../fields/CursorField'; import { AclPrivate, Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc'; diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 08ffa1466..2bdcf472f 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -15,6 +15,7 @@ import { Docs, DocUtils } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType } from '../../util/DragManager'; +import { LinkManager } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; @@ -708,7 +709,7 @@ export class TreeView extends React.Component { @computed get validExpandViewTypes() { const annos = () => (DocListCast(this.doc[this.fieldKey + '-annotations']).length && !this.props.treeView.dashboardMode ? 'annotations' : ''); - const links = () => (DocListCast(this.doc.links).length && !this.props.treeView.dashboardMode ? 'links' : ''); + const links = () => (LinkManager.Links(this.doc).length && !this.props.treeView.dashboardMode ? 'links' : ''); const data = () => (this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : ''); const aliases = () => (this.props.treeView.dashboardMode ? '' : 'aliases'); const fields = () => (Doc.noviceMode ? '' : 'fields'); diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index c9112eec3..3f6369898 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -1,7 +1,6 @@ import { action, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Doc } from '../../../fields/Doc'; -import { DocCast } from '../../../fields/Types'; import { LinkManager } from '../../util/LinkManager'; import { DocumentView } from '../nodes/DocumentView'; import { LinkDocPreview } from '../nodes/LinkDocPreview'; diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index cdac91a62..4741fc6f2 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, DocListCast } from '../../../fields/Doc'; +import { Doc } from '../../../fields/Doc'; import { Cast, DocCast, StrCast } from '../../../fields/Types'; import { WebField } from '../../../fields/URLField'; import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils'; @@ -15,11 +15,11 @@ import { LinkManager } from '../../util/LinkManager'; import { SelectionManager } from '../../util/SelectionManager'; import { SettingsManager } from '../../util/SettingsManager'; import { undoBatch } from '../../util/UndoManager'; +import { MainView } from '../MainView'; import { DocumentView, OpenWhere } from '../nodes/DocumentView'; import { LinkDocPreview } from '../nodes/LinkDocPreview'; import './LinkMenuItem.scss'; import React = require('react'); -import { MainView } from '../MainView'; interface LinkMenuItemProps { groupType: string; @@ -34,7 +34,7 @@ interface LinkMenuItemProps { // drag links and drop link targets (aliasing them if needed) export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: DocumentView, downX: number, downY: number, sourceDoc: Doc, specificLinks?: Doc[]) { - const draggedDocs = (specificLinks ? specificLinks : DocListCast(sourceDoc.links)).map(link => LinkManager.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[]; + const draggedDocs = (specificLinks ? specificLinks : LinkManager.Links(sourceDoc)).map(link => LinkManager.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[]; if (draggedDocs.length) { const moddrag: Doc[] = []; diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 4c26468b9..890ecc1b2 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -11,6 +11,7 @@ import { emptyFunction, formatTime, OmitKeys, returnFalse, setupMoveUpEvents } f import { DocUtils } from '../../documents/Documents'; import { Networking } from '../../Network'; import { DragManager } from '../../util/DragManager'; +import { LinkManager } from '../../util/LinkManager'; import { undoBatch } from '../../util/UndoManager'; import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionStackedTimeline'; import { ContextMenu } from '../ContextMenu'; @@ -84,7 +85,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent (this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton)), icon: 'eye' }); !appearance && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' }); @@ -969,7 +969,7 @@ export class DocumentViewInternal extends DocComponent this.toggleFollowLink(false, true), icon: 'map-pin' }); onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' }); !existingOnClick && cm.addItem({ description: 'OnClick...', addDivider: true, noexpand: true, subitems: onClicks, icon: 'mouse-pointer' }); - } else if (DocListCast(this.Document.links).length) { + } else if (LinkManager.Links(this.Document).length) { onClicks.push({ description: 'Select on Click', event: () => this.selectOnClick(), icon: 'link' }); onClicks.push({ description: 'Follow Link on Click', event: () => this.followLinkOnClick(undefined, false), icon: 'link' }); onClicks.push({ description: 'Toggle Link Target on Click', event: () => this.toggleTargetOnClick(), icon: 'map-pin' }); @@ -1907,7 +1907,7 @@ ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc) { const linkSource = Cast(linkCollection.linkSource, Doc, null); const collectedLinks = DocListCast(Doc.GetProto(linkCollection).data); let wid = linkSource[WidthSym](); - const links = DocListCast(linkSource.links); + const links = LinkManager.Links(linkSource); links.forEach(link => { const other = LinkManager.getOppositeAnchor(link, linkSource); const otherdoc = !other ? undefined : other.annotationOn ? Cast(other.annotationOn, Doc, null) : other; diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index bbe9b4323..16b352e4f 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -3,7 +3,7 @@ import { Tooltip } from '@material-ui/core'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import wiki from 'wikijs'; -import { Doc, DocCastAsync, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { Doc, DocCastAsync, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils'; import { DocServer } from '../../DocServer'; @@ -14,11 +14,10 @@ import { LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; import { SettingsManager } from '../../util/SettingsManager'; import { Transform } from '../../util/Transform'; +import { SearchBox } from '../search/SearchBox'; import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView'; import './LinkDocPreview.scss'; import React = require('react'); -import { SearchUtil } from '../../util/SearchUtil'; -import { SearchBox } from '../search/SearchBox'; interface LinkDocPreviewProps { linkDoc?: Doc; @@ -110,8 +109,8 @@ export class LinkDocPreview extends React.Component { const anchorDoc = anchorDocId ? PromiseValue(DocCast(DocServer.GetCachedRefField(anchorDocId) ?? DocServer.GetRefField(anchorDocId))) : undefined; anchorDoc?.then?.( action(anchor => { - if (anchor instanceof Doc && DocListCast(anchor.links).length) { - this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0]; + if (anchor instanceof Doc && LinkManager.Links(anchor).length) { + this._linkDoc = this._linkDoc ?? LinkManager.Links(anchor)[0]; const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords; if (automaticLink) { // automatic links specify the target in the link info, not the source diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 7df3d5f24..dc6bf6f9e 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, untracked } from 'mobx'; import { observer } from 'mobx-react'; import { basename } from 'path'; -import { Doc, DocListCast, HeightSym, WidthSym } from '../../../fields/Doc'; +import { Doc, HeightSym, WidthSym } from '../../../fields/Doc'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; @@ -14,6 +14,7 @@ import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { Networking } from '../../Network'; import { DocumentManager } from '../../util/DocumentManager'; +import { LinkManager } from '../../util/LinkManager'; import { ReplayMovements } from '../../util/ReplayMovements'; import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; @@ -85,7 +86,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { + LinkManager.Links(this.dataDoc).forEach((l, i) => { const anchor = (l.anchor1 as Doc).annotationOn ? (l.anchor1 as Doc) : (l.anchor2 as Doc).annotationOn ? (l.anchor2 as Doc) : undefined; if (anchor && (anchor.annotationOn as Doc).mediaState === 'recording') { linkTime = NumCast(anchor._timecodeToShow /* audioStart */); @@ -400,7 +400,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { const newAutoLinks = new Set(); - const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship === LinkManager.AutoKeywords); + const oldAutoLinks = LinkManager.Links(this.props.Document).filter(link => link.linkRelationship === LinkManager.AutoKeywords); if (this._editorView?.state.doc.textContent) { const isNodeSel = this._editorView.state.selection instanceof NodeSelection; const f = this._editorView.state.selection.from; @@ -453,7 +453,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { alink = alink ?? - (DocListCast(this.Document.links).find(link => Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || + (LinkManager.Links(this.Document).find(link => Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, LinkManager.AutoKeywords)!); newAutoLinks.add(alink); const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }]; @@ -687,7 +687,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - LinkManager.Instance.deleteLink(DocListCast(anchor.links)[0]); + LinkManager.Instance.deleteLink(LinkManager.Links(anchor)[0]); // const docAnnotations = DocListCast(this.props.dataDoc[this.fieldKey]); // this.props.dataDoc[this.fieldKey] = new List(docAnnotations.filter(a => a !== this.annoTextRegion)); // AnchorMenu.Instance.fadeOut(true); @@ -1014,7 +1014,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent DocumentManager.Instance.RecordingEvent, this.breakupDictation); this._disposers.autoHeight = reaction( () => this.autoHeight, @@ -1041,7 +1041,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent DocListCast(this.dataDoc.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks + () => LinkManager.Links(this.dataDoc), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks newLinks => { this._cachedLinks.forEach(l => !newLinks.includes(l) && this.RemoveLinkFromDoc(l)); this._cachedLinks = newLinks; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index dda96d8e7..be40b3592 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -35,7 +35,6 @@ import { FieldView, FieldViewProps } from '../FieldView'; import { ScriptingBox } from '../ScriptingBox'; import './PresBox.scss'; import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums'; -import { tag } from 'xregexp/types'; const { Howl } = require('howler'); export interface pinDataTypes { @@ -1551,7 +1550,7 @@ export class PresBox extends ViewBoxBaseComponent() { const type = DocCast(tagDoc?.annotationOn)?.type ?? tagDoc.type; activeItem.presIndexedStart = type === DocumentType.COL ? 1 : 0; // a progressivized slide doesn't have sub-slides, but rather iterates over the data list of the target being progressivized. - // to avoid creating a new slide to correspond to each of the target's data list, we simply reference the target's data list. + // to avoid creating a new slide to correspond to each of the target's data list, we create a computedField to refernce the target's data list. let dataField = Doc.LayoutFieldKey(tagDoc); if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField = dataField + '-annotations'; diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 3ba60edd7..fe9b13fbe 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -5,7 +5,6 @@ import * as React from 'react'; import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { DocCast, StrCast } from '../../../fields/Types'; -import { StopEvent } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; @@ -470,7 +469,7 @@ export class SearchBox extends ViewBoxBaseComponent() { } } style={{ - fontWeight: DocListCast(fromDoc?.links).find( + fontWeight: LinkManager.Links(fromDoc).find( link => Doc.AreProtosEqual(LinkManager.getOppositeAnchor(link, fromDoc!), result[0] as Doc) || Doc.AreProtosEqual(DocCast(LinkManager.getOppositeAnchor(link, fromDoc!)?.annotationOn), result[0] as Doc) ) ? 'bold' diff --git a/src/fields/util.ts b/src/fields/util.ts index e517e7604..6ff13d5d3 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -1,8 +1,8 @@ -import { forEach } from 'lodash'; import { $mobx, action, observable, runInAction, trace } from 'mobx'; import { computedFn } from 'mobx-utils'; import { DocServer } from '../client/DocServer'; import { CollectionViewType } from '../client/documents/DocumentTypes'; +import { LinkManager } from '../client/util/LinkManager'; import { SerializationHelper } from '../client/util/SerializationHelper'; import { UndoManager } from '../client/util/UndoManager'; import { returnZero } from '../Utils'; @@ -18,7 +18,6 @@ import { Doc, DocListCast, DocListCastAsync, - FieldResult, ForceServerWrite, HeightSym, HierarchyMapping, @@ -259,7 +258,7 @@ function getEffectiveAcl(target: any, user?: string): symbol { */ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean, visited?: Doc[], isDashboard?: boolean) { if (!visited) visited = [] as Doc[]; - if (visited.includes(target)) return; + if (!target || visited.includes(target)) return; if ((target._viewType === CollectionViewType.Docking && visited.length > 1) || Doc.GetProto(visited[0]) !== Doc.GetProto(target)) { target[key] = acl; @@ -293,26 +292,18 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc } // maps over the links of the document - DocListCast(dataDoc.links).forEach(link => distributeAcls(key, acl, link, inheritingFromCollection, visited)); + LinkManager.Links(dataDoc).forEach(link => distributeAcls(key, acl, link, inheritingFromCollection, visited)); // maps over the children of the document - DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + (isDashboard ? '-all' : '')]).map(d => { + DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => { distributeAcls(key, acl, d, inheritingFromCollection, visited); - // } - const data = d[DataSym]; - if (data) { - distributeAcls(key, acl, data, inheritingFromCollection, visited); - } + distributeAcls(key, acl, d[DataSym], inheritingFromCollection, visited); }); // maps over the annotations of the document - DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '-annotations']).map(d => { + DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '-annotations']).forEach(d => { distributeAcls(key, acl, d, inheritingFromCollection, visited); - // } - const data = d[DataSym]; - if (data) { - distributeAcls(key, acl, data, inheritingFromCollection, visited); - } + distributeAcls(key, acl, d[DataSym], inheritingFromCollection, visited); }); } -- 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/util') 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 8aeab8c3f25c556e39f3e9e58f9c321e79459df8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 17 Mar 2023 12:22:03 -0400 Subject: fixed scriptingbox to remove script if text is empty. fixed scripting with capturedvariables not to cache scripts with lists of captured documents. fixed runtime warnings with stackedTimelined and AudioBox --- src/client/util/Scripting.ts | 8 +++++++- src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/MainView.tsx | 3 +-- src/client/views/StyleProvider.tsx | 2 +- src/client/views/TemplateMenu.tsx | 1 - .../views/collections/CollectionStackedTimeline.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 10 +++------- src/client/views/nodes/DocumentView.tsx | 4 ---- src/client/views/nodes/ScriptingBox.tsx | 16 +++++++++------- src/fields/Doc.ts | 9 +-------- 10 files changed, 24 insertions(+), 33 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index d32298c83..f17a98616 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -7,7 +7,9 @@ import * as typescriptlib from '!!raw-loader!./type_decls.d'; import * as ts from 'typescript'; import { Doc, Field } from '../../fields/Doc'; +import { ToScriptString } from '../../fields/FieldSymbols'; import { ObjectField } from '../../fields/ObjectField'; +import { RefField } from '../../fields/RefField'; import { ScriptField } from '../../fields/ScriptField'; import { scriptingGlobals, ScriptingGlobals } from './ScriptingGlobals'; export { ts }; @@ -180,7 +182,11 @@ function forEachNode(node: ts.Node, onEnter: Traverser, onExit?: Traverser, inde export function CompileScript(script: string, options: ScriptOptions = {}): CompileResult { const captured = options.capturedVariables ?? {}; - const signature = Object.keys(captured).reduce((p, v) => p + `${v}=${captured[v] instanceof ObjectField ? 'XXX' : captured[v].toString()}`, ''); + const signature = Object.keys(captured).reduce((p, v) => { + const formatCapture = (obj: any) => `${v}=${obj instanceof RefField ? 'XXX' : obj.toString()}`; + if (captured[v] instanceof Array) return p + (captured[v] as any).map(formatCapture); + return p + formatCapture(captured[v]); + }, ''); const found = ScriptField.GetScriptFieldCache(script + ':' + signature); if (found) return found as CompiledScript; const { requiredType = '', addReturn = false, params = {}, capturedVariables = {}, typecheck = true } = options; diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index af868ba9c..9389fed01 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -518,7 +518,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV ) }>
- {} +
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 2e04ca3dd..118635a38 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -10,7 +10,7 @@ import 'normalize.css'; import * as React from 'react'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { ScriptField } from '../../fields/ScriptField'; -import { DocCast, StrCast } from '../../fields/Types'; +import { StrCast } from '../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; @@ -57,7 +57,6 @@ import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup'; import { LinkDocPreview } from './nodes/LinkDocPreview'; import { RadialMenu } from './nodes/RadialMenu'; import { TaskCompletionBox } from './nodes/TaskCompletedBox'; -import { PresBox } from './nodes/trails'; import { OverlayView } from './OverlayView'; import { AnchorMenu } from './pdf/AnchorMenu'; import { PreviewCursor } from './PreviewCursor'; diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index dba5d703f..b11117b11 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -5,7 +5,7 @@ import { action, runInAction } from 'mobx'; import { extname } from 'path'; import { Doc, Opt } from '../../fields/Doc'; import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../fields/Types'; -import { DashColor, emptyFunction, lightOrDark } from '../../Utils'; +import { DashColor, lightOrDark } from '../../Utils'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { DocFocusOrOpen } from '../util/DocumentManager'; import { LinkManager } from '../util/LinkManager'; diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 863829a51..681ff66e0 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -1,7 +1,6 @@ import { action, computed, observable, ObservableSet, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, DocListCast } from '../../fields/Doc'; -import { List } from '../../fields/List'; import { ScriptField } from '../../fields/ScriptField'; import { Cast, StrCast } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index f9249e7d5..302d4a464 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -565,7 +565,7 @@ export class CollectionStackedTimeline extends CollectionSubView Math.max(m, o.level), 0) + 2; - return ( + return this.clipDuration === 0 ? null : (
{ - e.stopPropagation(); - }} - onChange={(e: React.ChangeEvent) => { - this.zoom(Number(e.target.value)); - }} + onPointerDown={(e: React.PointerEvent) => e.stopPropagation()} + onChange={(e: React.ChangeEvent) => this.zoom(Number(e.target.value))} />
)} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6ae102a0c..b7a760c1e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1489,10 +1489,6 @@ export class DocumentViewInternal extends DocComponent (params[p.split(':')[0].trim()] = p.split(':')[1].trim())); - const result = CompileScript(this.rawText, { - editable: true, - transformer: DocumentIconContainer.getTransformer(), - params, - typecheck: false, - }); - Doc.SetInPlace(this.rootDoc, this.fieldKey, result.compiled ? new ScriptField(result, undefined, this.rawText) : new ScriptField(undefined, undefined, this.rawText), true); + const result = !this.rawText.trim() + ? ({ compiled: false, errors: undefined } as any) + : CompileScript(this.rawText, { + editable: true, + transformer: DocumentIconContainer.getTransformer(), + params, + typecheck: false, + }); + Doc.SetInPlace(this.rootDoc, this.fieldKey, result.compiled ? new ScriptField(result, undefined, this.rawText) : undefined, true); this.onError(result.compiled ? undefined : result.errors); return result.compiled; }; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 3169031b4..44314dca2 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -518,20 +518,13 @@ export namespace Doc { } export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) { if (key.startsWith('_')) key = key.substring(1); - const hasProto = doc.proto instanceof Doc; + 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; if (onDeleg || !hasProto || (!onProto && !defaultProto)) { doc[key] = value; } else doc.proto![key] = value; } - export async function SetOnPrototype(doc: Doc, key: string, value: Field) { - const proto = Object.getOwnPropertyNames(doc).indexOf('isPrototype') === -1 ? doc.proto : doc; - - if (proto) { - proto[key] = value; - } - } export function GetAllPrototypes(doc: Doc): Doc[] { const protos: Doc[] = []; let d: Opt = doc; -- cgit v1.2.3-70-g09d2 From c885ae59ea378648dcc70b6f17dac2d3999c60b1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 17 Mar 2023 17:03:32 -0400 Subject: fixed clicking and dragging stackedTimeline anchors. updated followLink parameters --- src/client/util/LinkFollower.ts | 2 +- .../collections/CollectionStackedTimeline.tsx | 39 ++++++++++++---------- src/client/views/linking/LinkMenuItem.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 17 ++++------ src/client/views/nodes/LinkAnchorBox.tsx | 2 +- src/client/views/nodes/LinkDocPreview.tsx | 2 +- src/client/views/pdf/Annotation.tsx | 2 +- 7 files changed, 33 insertions(+), 33 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index eacbcc0e3..785018990 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -27,7 +27,7 @@ export class LinkFollower { // follows a link - if the target is on screen, it highlights/pans to it. // if the target isn't onscreen, then it will open up the target in the lightbox, or in place // depending on the followLinkLocation property of the source (or the link itself as a fallback); - public static FollowLink = (linkDoc: Opt, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean) => { + public static FollowLink = (linkDoc: Opt, sourceDoc: Doc, altKey: boolean) => { const batch = UndoManager.StartBatch('follow link click'); runInAction(() => (DocumentDecorations.Instance.overrideBounds = true)); // turn off decoration bounds while following links since animations may occur, and DocDecorations is based on screenToLocal which is not always an observable value LinkFollower.traverseLink( diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 302d4a464..d4e83f609 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -9,7 +9,7 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { Cast, NumCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; -import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, returnTrue, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils'; +import { emptyFunction, formatTime, OmitKeys, returnFalse, returnNone, returnOne, returnTrue, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; @@ -180,12 +180,15 @@ export class CollectionStackedTimeline extends CollectionSubView { - const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.25; + const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.05; const endTime = this.anchorEnd(anchorDoc); if (this.layoutDoc.autoPlayAnchors) { if (this.props.playing()) this.props.Pause(); @@ -449,9 +452,9 @@ export class CollectionStackedTimeline extends CollectionSubView { if (anchorDoc.isLinkButton) { - LinkFollower.FollowLink(undefined, anchorDoc, this.props, false); + LinkFollower.FollowLink(undefined, anchorDoc, false); } - const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.25; + const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.05; const endTime = this.anchorEnd(anchorDoc); if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 && endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4) { if (this.props.playing()) this.props.Pause(); @@ -598,10 +601,7 @@ export class CollectionStackedTimeline extends CollectionSubView { - this.props.playFrom(start, this.anchorEnd(d.anchor)); - e.stopPropagation(); + pointerEvents: 'none', }}> time < NumCast(this.props.mark[this.props.endTag]) && this._lastTimecode < NumCast(this.props.mark[this.props.startTag]) - 1e-5 ) { - LinkFollower.FollowLink(undefined, this.props.mark, this.props as any as DocumentViewProps, false); + LinkFollower.FollowLink(undefined, this.props.mark, false); } this._lastTimecode = time; } @@ -765,7 +765,9 @@ class StackedTimelineAnchor extends React.Component this._disposer?.(); } + @observable noEvents = false; // starting the drag event for anchor resizing + @action onAnchorDown = (e: React.PointerEvent, anchor: Doc, left: boolean): void => { //this.props._timeline?.setPointerCapture(e.pointerId); const newTime = (e: PointerEvent) => { @@ -783,8 +785,8 @@ class StackedTimelineAnchor extends React.Component } return false; }; + this.noEvents = true; var undo: UndoManager.Batch | undefined; - setupMoveUpEvents( this, e, @@ -793,11 +795,11 @@ class StackedTimelineAnchor extends React.Component this.props.setTime(newTime(e)); return changeAnchor(anchor, left, newTime(e)); }, - e => { + action(e => { this.props.setTime(newTime(e)); - // this.props._timeline?.releasePointerCapture(e.pointerId); undo?.end(); - }, + this.noEvents = false; + }), emptyFunction ); }; @@ -828,6 +830,7 @@ class StackedTimelineAnchor extends React.Component ref={action((r: DocumentView | null) => (anchor.view = r))} Document={mark} DataDoc={undefined} + pointerEvents={this.noEvents ? returnNone : undefined} styleProvider={this.props.styleProvider} renderDepth={this.props.renderDepth + 1} LayoutTemplate={undefined} @@ -858,15 +861,15 @@ class StackedTimelineAnchor extends React.Component render() { const inner = this.renderInner(this.props.mark, this.props.rangeClickScript, this.props.rangePlayScript, this.anchorScreenToLocalXf, this.width, this.height); return ( - <> +
{inner.view} {!inner.anchor.view || !SelectionManager.IsSelected(inner.anchor.view) ? null : ( <> -
this.onAnchorDown(e, this.props.mark, true)} /> -
this.onAnchorDown(e, this.props.mark, false)} /> +
this.onAnchorDown(e, this.props.mark, true)} /> +
this.onAnchorDown(e, this.props.mark, false)} /> )} - +
); } } diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 4741fc6f2..29e7cd3ad 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -140,7 +140,7 @@ export class LinkMenuItem extends React.Component { : undefined; if (focusDoc) this.props.docView.props.focus(focusDoc, { instant: true }); - LinkFollower.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false); + LinkFollower.FollowLink(this.props.linkDoc, this.props.sourceDoc, false); } } ); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b7a760c1e..805e58cd0 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -655,7 +655,7 @@ export class DocumentViewInternal extends DocComponent 0)) { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part @@ -1136,16 +1136,13 @@ export class DocumentViewInternal extends DocComponent {!this._retryThumb || !this.thumbShown() ? null : ( diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 1b37dc7ab..e12548f18 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -39,7 +39,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent() { this.onPointerMove, emptyFunction, (e, doubleTap) => { - if (doubleTap) LinkFollower.FollowLink(this.rootDoc, anchorContainerDoc, this.props, false); + if (doubleTap) LinkFollower.FollowLink(this.rootDoc, anchorContainerDoc, false); else this.props.select(false); }, false diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 16b352e4f..fcc5b6975 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -171,7 +171,7 @@ export class LinkDocPreview extends React.Component { followLink = () => { LinkDocPreview.Clear(); if (this._linkDoc && this._linkSrc) { - LinkFollower.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false); + LinkFollower.FollowLink(this._linkDoc, this._linkSrc, false); } else if (this.props.hrefs?.length) { const webDoc = Array.from(SearchBox.staticSearchCollection(Doc.MyFilesystem, this.props.hrefs[0]).keys()).lastElement() ?? diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 3b101a0c6..d1f3397f5 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -82,7 +82,7 @@ class RegionAnnotation extends React.Component { e.stopPropagation(); } else if (e.button === 0) { e.stopPropagation(); - LinkFollower.FollowLink(undefined, this.annoTextRegion, this.props, false); + LinkFollower.FollowLink(undefined, this.annoTextRegion,false); } }; -- cgit v1.2.3-70-g09d2 From 081f328c117ffdf7ab284be86cdf0342041e7708 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 20 Mar 2023 15:32:06 -0400 Subject: cleaned up pointer events so that nested documents can be selected directly without selecting their container. fixed following link to video timeline marker. fixed focusing on groups. added didMove to DocFocusOptions to restore ability to do toggle on/off of target. fixed lockingPosition of ink strokes. fixed clicking on inkstrokes in groups to use visiblePainted instead of all for pointer events. --- src/client/documents/Documents.ts | 3 ++- src/client/util/DocumentManager.ts | 3 ++- src/client/views/DocComponent.tsx | 5 +++++ src/client/views/InkingStroke.tsx | 2 +- src/client/views/PropertiesView.tsx | 10 ++++----- src/client/views/StyleProvider.tsx | 25 +++++++++++----------- .../collections/CollectionStackedTimeline.tsx | 5 +---- .../collectionFreeForm/CollectionFreeFormView.tsx | 15 ++++++++++--- .../views/nodes/CollectionFreeFormDocumentView.tsx | 1 - src/client/views/nodes/DocumentContentsView.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 22 +++++++++---------- src/client/views/nodes/button/FontIconBox.tsx | 2 +- src/client/views/pdf/Annotation.tsx | 6 +++--- src/fields/Doc.ts | 8 +++---- 14 files changed, 62 insertions(+), 49 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ff6c8d440..3e89c8347 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -584,7 +584,7 @@ export namespace Docs { [ DocumentType.FONTICON, { - layout: { view: FontIconBox, dataField: defaultDataKey }, + layout: { view: FontIconBox, dataField: 'icon' }, options: { allowClickBeforeDoubleClick: true, hideLinkButton: true, _width: 40, _height: 40 }, }, ], @@ -1686,6 +1686,7 @@ export namespace DocUtils { x: Cast(doc.x, 'number', null), y: Cast(doc.y, 'number', null), backgroundColor: '#ACCEF7', + hideAllLinks: true, _width: 15, _height: 15, _xPadding: 0, diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index f2c554866..947613801 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -265,6 +265,7 @@ export class DocumentManager { let rootContextView = await new Promise(res => { const viewIndex = docContextPath.findIndex(doc => this.getDocumentView(doc)); if (viewIndex !== -1) return res(this.getDocumentView(docContextPath[viewIndex])!); + options.didMove = true; docContextPath.some(doc => TabDocView.Activate(doc)) || MainView.addDocTabFunc(docContextPath[0], options.openLocation as OpenWhere); this.AddViewRenderedCb(docContextPath[0], dv => res(dv)); }); @@ -299,7 +300,7 @@ export class DocumentManager { PresBox.restoreTargetDocView(docView, viewSpec, options.zoomTime ?? 500); Doc.linkFollowHighlight(docView.rootDoc, undefined, options.effect); if (options.playAudio) DocumentManager.playAudioAnno(docView.rootDoc); - if (options.toggleTarget) docView.rootDoc.hidden = !docView.rootDoc.hidden; + if (options.toggleTarget && (!options.didMove || docView.rootDoc.hidden)) docView.rootDoc.hidden = !docView.rootDoc.hidden; if (options.effect) docView.rootDoc[AnimationSym] = options.effect; if (options.zoomTextSelections && Doc.UnhighlightTimer && contextView && viewSpec.textHtml) { diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 7c81d92d4..0b92fd864 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -16,6 +16,7 @@ import { Touchable } from './Touchable'; /// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView) export interface DocComponentProps { Document: Doc; + fieldKey?: string; LayoutTemplate?: () => Opt; LayoutTemplateString?: string; } @@ -37,6 +38,10 @@ export function DocComponent

() { @computed get dataDoc() { return this.props.Document[DataSym] as Doc; } + // key where data is stored + @computed get fieldKey() { + return this.props.fieldKey; + } protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 4f08a8e22..3861331b5 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -418,7 +418,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { inkScaleX, inkScaleY, '', - this.props.pointerEvents?.() ?? (this.rootDoc._lockedPosition ? 'none' : 'visiblepainted'), + this.props.pointerEvents?.() ?? 'visiblepainted', 0.0, false, downHdlr, diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 03b4100a7..f3a5a5393 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1678,8 +1678,8 @@ export class PropertiesView extends React.Component {

Center Target (no zoom)

Zoom %

-
+
this.setZoom(String(zoom), 0.1))}> @@ -1706,11 +1706,11 @@ export class PropertiesView extends React.Component {
- {!targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)} + {!targZoom ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
, props: Opt doc && BoolCast(doc._lockedPosition); + const lockedPosition = () => doc && BoolCast(doc._lockedPosition); const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor); const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity); const showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle); @@ -268,7 +268,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt, props: Opt 0 ? ( + return doc && doc.pointerEvents === 'none' && lockedPosition() && !Doc.IsSystem(doc) && (props?.renderDepth || 0) > 0 ? (
toggleLockedPosition(doc)}> - +
) : null; } diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index d4e83f609..4941bc722 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -817,10 +817,7 @@ class StackedTimelineAnchor extends React.Component // renders anchor LabelBox renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) { const anchor = observable({ view: undefined as any }); - const focusFunc = (doc: Doc, options: DocFocusOptions) => { - this.props.playLink(mark); - this.props.focus(doc, options); - }; + const focusFunc = (doc: Doc, options: DocFocusOptions) => this.props.playLink(mark); return { anchor, view: ( diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f0c140ef1..ac90c67a5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -293,6 +293,13 @@ export class CollectionFreeFormView extends CollectionSubView= -1e-4 && curTime <= endTime); } + groupFocus = (anchor: Doc, options: DocFocusOptions) => { + options.docTransform = new Transform(-NumCast(this.rootDoc.panX) + NumCast(anchor.x), -NumCast(this.rootDoc.panY) + NumCast(anchor.y), 1); + const res = this.props.focus(this.rootDoc, options); + options.docTransform = undefined; + return res; + }; + focus = (anchor: Doc, options: DocFocusOptions) => { const xfToCollection = options?.docTransform ?? Transform.Identity(); const savedState = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined }; @@ -301,6 +308,7 @@ export class CollectionFreeFormView extends CollectionSubView SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' }); if (!Doc.noviceMode) { moreItems.push({ description: 'Make View of Metadata Field', event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: 'concierge-bell' }); moreItems.push({ description: `${this.Document._chromeHidden ? 'Show' : 'Hide'} Chrome`, event: () => (this.Document._chromeHidden = !this.Document._chromeHidden), icon: 'project-diagram' }); @@ -1003,6 +1003,8 @@ export class DocumentViewInternal extends DocComponent Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: 'fingerprint' }); } moreItems.push({ description: 'Export collection', icon: 'download', event: async () => Doc.Zip(this.props.Document) }); + + (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && moreItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' }); } if (this.props.removeDocument && !Doc.IsSystem(this.rootDoc) && Doc.ActiveDashboard !== this.props.Document) { @@ -1130,6 +1132,7 @@ export class DocumentViewInternal extends DocComponent ); } + pointerEventsFunc = () => this.pointerEvents; @computed get contents() { TraceMobx(); return ( @@ -1137,12 +1140,9 @@ export class DocumentViewInternal extends DocComponent {!this._retryThumb || !this.thumbShown() ? null : ( @@ -1163,7 +1163,7 @@ export class DocumentViewInternal extends DocComponent {this.layoutDoc.hideAllLinks ? null : this.allLinkEndpoints} @@ -1501,7 +1501,7 @@ export class DocumentViewInternal extends DocComponent {!borderPath.path ? ( animRenderDoc diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index b3a3c3ae4..339887757 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -101,7 +101,7 @@ export class FontIconBox extends DocComponent() { return StrCast(this.rootDoc.label, StrCast(this.rootDoc.title)); } Icon = (color: string) => { - const icon = StrCast(this.dataDoc.icon, 'user') as any; + const icon = StrCast(this.dataDoc[this.fieldKey ?? 'icon'] ?? this.dataDoc.icon, 'user') as any; const trailsIcon = () => ; return !icon ? null : icon === 'pres-trail' ? trailsIcon() : ; }; diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index d1f3397f5..db6b1f011 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -1,5 +1,5 @@ import React = require('react'); -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { action, computed } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; @@ -7,10 +7,10 @@ import { List } from '../../../fields/List'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types'; import { LinkFollower } from '../../util/LinkFollower'; import { undoBatch } from '../../util/UndoManager'; +import { OpenWhere } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { AnchorMenu } from './AnchorMenu'; import './Annotation.scss'; -import { OpenWhere } from '../nodes/DocumentView'; interface IAnnotationProps extends FieldViewProps { anno: Doc; @@ -82,7 +82,7 @@ class RegionAnnotation extends React.Component { e.stopPropagation(); } else if (e.button === 0) { e.stopPropagation(); - LinkFollower.FollowLink(undefined, this.annoTextRegion,false); + LinkFollower.FollowLink(undefined, this.annoTextRegion, false); } }; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 44314dca2..40ef67f92 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -891,10 +891,10 @@ export namespace Doc { // img.file("smile.gif", imgData, {base64: true}); // Generate the zip file asynchronously - // zip.generateAsync({ type: 'blob' }).then((content: any) => { - // // Force down of the Zip file - // saveAs(content, doc.title + '.zip'); // glr: Possibly change the name of the document to match the title? - // }); + zip.generateAsync({ type: 'blob' }).then((content: any) => { + // Force down of the Zip file + saveAs(content, doc.title + '.zip'); // glr: Possibly change the name of the document to match the title? + }); } // // Determines whether the layout needs to be expanded (as a template). -- cgit v1.2.3-70-g09d2 From b25f333713706a7456ab418960082bcabe830f11 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 20 Mar 2023 20:48:38 -0400 Subject: fixed copying of docs with template layout docs. fixed clone to search through RTFs for referenced documents to clone. renamed nested documents docId instead of docid for consistency with other nested field ids. --- src/client/util/CurrentUserUtils.ts | 6 +-- src/client/views/GlobalKeyHandler.ts | 2 +- src/client/views/SidebarAnnos.tsx | 4 +- src/client/views/collections/CollectionSubView.tsx | 10 ++--- src/client/views/nodes/DocumentView.tsx | 6 +-- .../nodes/formattedText/DashDocCommentView.tsx | 16 ++++---- .../views/nodes/formattedText/DashDocView.tsx | 16 ++++---- .../views/nodes/formattedText/DashFieldView.tsx | 8 ++-- .../views/nodes/formattedText/FormattedTextBox.tsx | 8 ++-- .../views/nodes/formattedText/RichTextRules.ts | 26 ++++++------ src/client/views/nodes/formattedText/nodes_rts.ts | 8 ++-- src/fields/Doc.ts | 46 ++++++++++++++-------- src/fields/RichTextField.ts | 26 ++++++------ src/fields/RichTextUtils.ts | 10 ++--- 14 files changed, 103 insertions(+), 89 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 2820c66ee..c038fd6ab 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -148,7 +148,7 @@ export class CurrentUserUtils { { noteType: "Idea", backgroundColor: "pink", icon: "lightbulb" }, { noteType: "Topic", backgroundColor: "lightblue", icon: "book-open" }]; const reqdNoteList = reqdTempOpts.map(opts => { - const reqdOpts = {...opts, title: "text", width:200, autoHeight: true, fitWidth: true, system: true}; + const reqdOpts = {...opts, title: "text", width:200, autoHeight: true, fitWidth: true}; const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined; return DocUtils.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note"); }); @@ -224,11 +224,11 @@ export class CurrentUserUtils { { type: "paragraph", attrs: {}, content: [{ type: "dashField", - attrs: { fieldKey: "author", docid: "", hideKey: false }, + attrs: { fieldKey: "author", docId: "", hideKey: false }, marks: [{ type: "strong" }] }, { type: "dashField", - attrs: { fieldKey: "creationDate", docid: "", hideKey: false }, + attrs: { fieldKey: "creationDate", docId: "", hideKey: false }, marks: [{ type: "strong" }] }] }] diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index f849b21e3..0f8b46dbe 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -372,7 +372,7 @@ export class KeyManager { list.push(doc); } if (count === docids.length) { - const added = await Promise.all(list.filter(d => !docList.includes(d)).map(async d => (clone ? (await Doc.MakeClone(d, ['links'])).clone : d))); + const added = await Promise.all(list.filter(d => !docList.includes(d)).map(async d => (clone ? (await Doc.MakeClone(d, true)).clone : d))); if (added.length) { added.map(doc => (doc.context = targetDataDoc)); undoBatch(() => { diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 7519cbb05..02dd15960 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -84,7 +84,7 @@ export class SidebarAnnos extends React.Component { Doc.GetProto(target)[key] = val; return { type: 'dashField', - attrs: { fieldKey: key, docid: '', hideKey: false, editable: true }, + attrs: { fieldKey: key, docId: '', hideKey: false, editable: true }, marks: [{ type: 'pFontSize', attrs: { fontSize: '12px' } }, { type: 'strong' }, { type: 'user_mark', attrs: { userid: Doc.CurrentUserEmail, modified: 0 } }], }; }); @@ -111,7 +111,7 @@ export class SidebarAnnos extends React.Component { { type: 'pFontSize', attrs: { fontSize: '8px' } }, { type: 'em' }, ], - attrs: { fieldKey: 'text', docid: anchor[Id], hideKey: true, editable: false }, + attrs: { fieldKey: 'text', docId: anchor[Id], hideKey: true, editable: false }, }, ], }, diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index e46220f02..bd74c9399 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -269,10 +269,10 @@ export function CollectionSubView(moreProps?: X) { if (FormattedTextBox.IsFragment(html)) { const href = FormattedTextBox.GetHref(html); if (href) { - const docid = FormattedTextBox.GetDocFromUrl(href); - if (docid) { + const docId = FormattedTextBox.GetDocFromUrl(href); + if (docId) { // prosemirror text containing link to dash document - DocServer.GetRefField(docid).then(f => { + DocServer.GetRefField(docId).then(f => { if (f instanceof Doc) { if (options.x || options.y) { f.x = options.x as number; @@ -311,8 +311,8 @@ export function CollectionSubView(moreProps?: X) { } else { const path = window.location.origin + '/doc/'; if (text.startsWith(path)) { - const docid = text.replace(Doc.globalServerPath(), '').split('?')[0]; - DocServer.GetRefField(docid).then(f => { + const docId = text.replace(Doc.globalServerPath(), '').split('?')[0]; + DocServer.GetRefField(docId).then(f => { if (f instanceof Doc) { if (options.x || options.y) { f.x = options.x as number; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 36e8facf5..edf508c95 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1578,8 +1578,8 @@ export class DocumentView extends React.Component { } public static showBackLinks(linkSource: Doc) { - const docid = Doc.CurrentUserEmail + Doc.GetProto(linkSource)[Id] + '-pivotish'; - DocServer.GetRefField(docid).then(docx => { + const docId = Doc.CurrentUserEmail + Doc.GetProto(linkSource)[Id] + '-pivotish'; + DocServer.GetRefField(docId).then(docx => { const rootAlias = () => { const rootAlias = Doc.MakeAlias(linkSource); rootAlias.x = rootAlias.y = 0; @@ -1592,7 +1592,7 @@ export class DocumentView extends React.Component { /*rootAlias()*/ ], { title: linkSource.title + '-pivot', _width: 500, _height: 500 }, - docid + docId ); linkCollection.linkSource = linkSource; if (!linkCollection.reactionScript) linkCollection.reactionScript = ScriptField.MakeScript('updateLinkCollection(self)'); diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx index fcd6e0c55..aa269d8d6 100644 --- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx +++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx @@ -32,7 +32,7 @@ export class DashDocCommentView { }; this.root = ReactDOM.createRoot(this.dom); - this.root.render(); + this.root.render(); (this as any).dom = this.dom; } @@ -48,7 +48,7 @@ export class DashDocCommentView { } interface IDashDocCommentViewInternal { - docid: string; + docId: string; view: any; getPos: any; } @@ -63,13 +63,13 @@ export class DashDocCommentViewInternal extends React.Component dashDoc instanceof Doc && Doc.linkFollowUnhighlight()); + DocServer.GetRefField(this.props.docId).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowUnhighlight()); e.preventDefault(); e.stopPropagation(); } onPointerEnterCollapsed(e: any) { - DocServer.GetRefField(this.props.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc, false)); + DocServer.GetRefField(this.props.docId).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc, false)); e.preventDefault(); e.stopPropagation(); } @@ -82,7 +82,7 @@ export class DashDocCommentViewInternal extends React.Component { - expand && DocServer.GetRefField(this.props.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc)); + expand && DocServer.GetRefField(this.props.docId).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc)); try { this.props.view.dispatch(this.props.view.state.tr.setSelection(TextSelection.create(this.props.view.state.tr.doc, this.props.getPos() + (expand ? 2 : 1)))); } catch (e) {} @@ -100,12 +100,12 @@ export class DashDocCommentViewInternal extends React.Component { try { @@ -119,7 +119,7 @@ export class DashDocCommentViewInternal extends React.Component