diff options
author | bobzel <zzzman@gmail.com> | 2023-12-05 14:13:02 -0500 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2023-12-05 14:13:02 -0500 |
commit | 3f412f469925f444714fd20a9bd76f4c36029d34 (patch) | |
tree | 4c0259357586cf88e56d5d3c20177aa6ed4b71db /src | |
parent | 304f7e25fb2a533876a59bca7215126d02d94dbf (diff) | |
parent | 23f789ab0bc9947f1bd23816183df2b5cc89b0e6 (diff) |
merged with master
Diffstat (limited to 'src')
105 files changed, 812 insertions, 997 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9107bd6ab..929660ff4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1469,7 +1469,7 @@ export namespace DocUtils { { 'acl-Guest': SharingPermissions.Augment, '_acl-Guest': SharingPermissions.Augment, - title: ComputedField.MakeFunction('generateLinkTitle(self)') as any, + title: ComputedField.MakeFunction('generateLinkTitle(this)') as any, link_anchor_1_useSmallAnchor: source.useSmallAnchor ? true : undefined, link_anchor_2_useSmallAnchor: target.useSmallAnchor ? true : undefined, link_displayLine: linkSettings.link_displayLine, @@ -1678,7 +1678,7 @@ export namespace DocUtils { newDoc.x = x; newDoc.y = y; EquationBox.SelectOnLoad = newDoc[Id]; - if (newDoc.type === DocumentType.RTF) FormattedTextBox.SelectOnLoad = newDoc[Id]; + if (newDoc.type === DocumentType.RTF) FormattedTextBox.SetSelectOnLoad(newDoc); if (pivotField) { newDoc[pivotField] = pivotValue; } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 80ec1e7bb..d3ed4ca7c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -110,8 +110,8 @@ export class CurrentUserUtils { const tempClicks = DocCast(doc[field]); const reqdClickOpts:DocumentOptions = {_width: 300, _height:200, isSystem: true}; const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [ - { opts: { title: "Open In Target", targetScriptKey: "onChildClick"}, script: "docCastAsync(documentView?.props.docViewPath().lastElement()?.rootDoc.target).then((target) => target && (target.proto.data = new List([self])))"}, - { opts: { title: "Open Detail On Right", targetScriptKey: "onChildDoubleClick"}, script: `openDoc(self.doubleClickView.${OpenWhere.addRight})`}]; + { opts: { title: "Open In Target", targetScriptKey: "onChildClick"}, script: "docCastAsync(documentView?.props.docViewPath().lastElement()?.Document.target).then((target) => target && (target.proto.data = new List([self])))"}, + { opts: { title: "Open Detail On Right", targetScriptKey: "onChildDoubleClick"}, script: `openDoc(this.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; @@ -278,7 +278,7 @@ export class CurrentUserUtils { {key: "Script", creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }}, {key: "DataViz", creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts), opts: { _width: 300, _height: 300 }}, {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _layout_autoHeight: true, treeView_HideUnrendered: true}}, - {key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: "embed" as dropActionType, treeView_HideTitle: true, _layout_fitWidth:true, _chromeHidden: true, layout_boxShadow: "0 0" }}, + {key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: "embed" as dropActionType, treeView_HideTitle: true, _layout_fitWidth:true, layout_boxShadow: "0 0" }}, {key: "Tab", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }}, {key: "Slide", creator: opts => Docs.Create.TreeDocument([], opts), opts: { _width: 300, _height: 200, _type_collection: CollectionViewType.Tree, treeView_HasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, @@ -351,7 +351,7 @@ export class CurrentUserUtils { { title: "Shared", toolTip: "Shared Docs", target: Doc.MySharedDocs, ignoreClick: true, icon: "users", funcs: {badgeValue: badgeValue}}, { title: "Trails", toolTip: "Trails ⌘R", target: Doc.UserDoc(), ignoreClick: true, icon: "pres-trail", funcs: {target: getActiveDashTrails}}, { title: "User Doc", toolTip: "User Doc", target: this.setupUserDocView(doc, "myUserDocView"), ignoreClick: true, icon: "address-card",funcs: {hidden: "IsNoviceMode()"} }, - ].map(tuple => ({...tuple, scripts:{onClick: 'selectMainMenu(self)'}})); + ].map(tuple => ({...tuple, scripts:{onClick: 'selectMainMenu(this)'}})); } /// the empty panel that is filled with whichever left menu button's panel has been selected @@ -480,14 +480,14 @@ export class CurrentUserUtils { const newDashboard = `createNewDashboard()`; const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, - title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", isSystem: true }; + title: "new Dash", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", isSystem: true }; const reqdBtnScript = {onClick: newDashboard,} const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.layout_headerButton), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript); const contextMenuScripts = [/*newDashboard*/] as string[]; const contextMenuLabels = [/*"Create New Dashboard"*/] as string[]; const contextMenuIcons = [/*"plus"*/] as string[]; - const childContextMenuScripts = [`toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(self)`, 'removeDashboard(self)', 'resetDashboard(self)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters + const childContextMenuScripts = [`toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(this)`, 'removeDashboard(this)', 'resetDashboard(this)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters const childContextMenuFilters = ['!IsNoviceMode()', '!IsNoviceMode()', undefined as any, undefined as any, '!IsNoviceMode()'];// entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuScripts const childContextMenuLabels = ["Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard", "Reset Dashboard"];// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters const childContextMenuIcons = ["tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters @@ -816,7 +816,7 @@ export class CurrentUserUtils { // When the user views one of these documents, it will be added to the sharing documents 'viewed' list field // The sharing document also stores the user's color value which helps distinguish shared documents from personal documents static setupSharedDocs(doc: Doc, sharingDocumentId: string) { - const dblClkScript = "{scriptContext.openLevel(documentView); addDocToList(documentView.props.treeViewDoc, 'viewed', documentView.rootDoc);}"; + const dblClkScript = "{scriptContext.openLevel(documentView); addDocToList(documentView.props.treeViewDoc, 'viewed', documentView.Document);}"; const sharedScripts = { treeView_ChildDoubleClick: dblClkScript, } const sharedDocOpts:DocumentOptions = { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 6e4de252d..162a0a11f 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -261,7 +261,7 @@ export namespace DragManager { // drags a linker button and creates a link on drop export function StartLinkDrag(ele: HTMLElement, sourceView: DocumentView, sourceDocGetAnchor: undefined | ((addAsAnnotation: boolean) => Doc), downX: number, downY: number, options?: DragOptions) { - StartDrag([ele], new DragManager.LinkDragData(sourceView, () => sourceDocGetAnchor?.(true) ?? sourceView.rootDoc), downX, downY, options); + StartDrag([ele], new DragManager.LinkDragData(sourceView, () => sourceDocGetAnchor?.(true) ?? sourceView.Document), downX, downY, options); } // drags a column from a schema view @@ -611,7 +611,7 @@ export namespace DragManager { ScriptingGlobals.add(function toggleRaiseOnDrag(readOnly?: boolean) { if (readOnly) { - return SelectionManager.Views().some(dv => dv.rootDoc.keepZWhenDragged); + return SelectionManager.Views().some(dv => dv.Document.keepZWhenDragged); } - SelectionManager.Views().map(dv => (dv.rootDoc.keepZWhenDragged = !dv.rootDoc.keepZWhenDragged)); + SelectionManager.Views().map(dv => (dv.Document.keepZWhenDragged = !dv.Document.keepZWhenDragged)); }); diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index 1a4c2450e..c45143f43 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -112,7 +112,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> { sizes.push(file.size); modifiedDates.push(file.lastModified); }); - collector.push(...(await Networking.UploadFilesToServer<Upload.ImageInformation>(batch.map(file =>({file}))))); + collector.push(...(await Networking.UploadFilesToServer<Upload.ImageInformation>(batch.map(file => ({ file }))))); runInAction(() => (this.completed += batch.length)); }); @@ -158,7 +158,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> { y: NumCast(doc.y) + offset, }; const parent = this.props.DocumentView?.().props.docViewPath().lastElement(); - if (parent?.rootDoc.type === DocumentType.COL) { + if (parent?.Document.type === DocumentType.COL) { let importContainer: Doc; if (docs.length < 50) { importContainer = Docs.Create.MasonryDocument(docs, options); diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 560d6b30f..e51770c25 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -9,7 +9,7 @@ import { StrCast } from '../../fields/Types'; export namespace SearchUtil { export type HighlightingResult = { [id: string]: { [key: string]: string[] } }; - export function SearchCollection(rootDoc: Opt<Doc>, query: string) { + export function SearchCollection(collectionDoc: Opt<Doc>, query: string) { const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; const blockedKeys = [ 'x', @@ -48,8 +48,8 @@ export namespace SearchUtil { query = query.toLowerCase(); const results = new Map<Doc, string[]>(); - if (rootDoc) { - const docs = DocListCast(rootDoc[Doc.LayoutFieldKey(rootDoc)]); + if (collectionDoc) { + const docs = DocListCast(collectionDoc[Doc.LayoutFieldKey(collectionDoc)]); const docIDs: String[] = []; SearchUtil.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => { const dtype = StrCast(doc.type) as DocumentType; diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index f7e6fa2dc..25f158f40 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -81,7 +81,7 @@ export namespace SelectionManager { return manager.SelectedSchemaDocument; } export function Docs(): Doc[] { - return manager.SelectedViews.map(dv => dv.rootDoc).filter(doc => doc?._type_collection !== CollectionViewType.Docking); + return manager.SelectedViews.map(dv => dv.Document).filter(doc => doc?._type_collection !== CollectionViewType.Docking); } } ScriptingGlobals.add(function SelectionManager_selectedDocType(type: string, expertMode: boolean, checkContext?: boolean) { diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 8d59426ec..34e294a4a 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -162,7 +162,7 @@ export class SharingManager extends React.Component<{}> { const { user, sharingDoc } = recipient; const target = targetDoc || this.targetDoc!; const acl = `acl-${normalizeEmail(user.email)}`; - const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc); + const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.Document); docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => { distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.upgradeNested ? true : undefined); if (permission !== SharingPermissions.None) { @@ -180,7 +180,7 @@ export class SharingManager extends React.Component<{}> { const target = targetDoc || this.targetDoc!; const acl = `acl-${normalizeEmail(StrCast(group.title))}`; - const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc); + const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.Document); docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => { distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.upgradeNested ? true : undefined); @@ -444,7 +444,7 @@ export class SharingManager extends React.Component<{}> { const users = this.individualSort === 'ascending' ? this.users.slice().sort(this.sortUsers) : this.individualSort === 'descending' ? this.users.slice().sort(this.sortUsers).reverse() : this.users; const groups = this.groupSort === 'ascending' ? groupList.slice().sort(this.sortGroups) : this.groupSort === 'descending' ? groupList.slice().sort(this.sortGroups).reverse() : groupList; - let docs = SelectionManager.Views().length < 2 ? [this.targetDoc] : SelectionManager.Views().map(docView => docView.rootDoc); + let docs = SelectionManager.Views().length < 2 ? [this.targetDoc] : SelectionManager.Views().map(docView => docView.Document); if (this.myDocAcls) { const newDocs: Doc[] = []; diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 2765e95e6..04fb1fe80 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -429,7 +429,7 @@ export class DashboardView extends React.Component { isSystem: true, layout_explainer: 'All of the trails that you have created will appear here.', }; - const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeView_ChildDoubleClick: 'openPresentation(documentView.rootDoc)' }); + const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeView_ChildDoubleClick: 'openPresentation(documentView.Document)' }); dashboardDoc.myTrails = new PrefetchProxy(myTrails); const contextMenuScripts = [reqdBtnScript.onClick]; diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index c04359d80..d104eb90c 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -16,7 +16,6 @@ import { CollectionFreeFormView } from './collections/collectionFreeForm'; /// 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<Doc>; LayoutTemplateString?: string; } @@ -34,10 +33,6 @@ export function DocComponent<P extends DocComponentProps>() { @computed get dataDoc() { return this.props.Document[DocData] as Doc; } - // key where data is stored - @computed get fieldKey() { - return this.props.fieldKey; - } } return Component; } @@ -45,7 +40,7 @@ export function DocComponent<P extends DocComponentProps>() { /// FieldViewBoxProps - a generic base class for field views that are not annotatable (e.g. InkingStroke, ColorBox) interface ViewBoxBaseProps { Document: Doc; - DataDoc?: Doc; + TemplateDataDocument?: Doc; DocumentView?: () => DocumentView; fieldKey: string; isSelected: () => boolean; @@ -65,7 +60,7 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() { } // This is the data part of a document -- ie, the data that is constant across all views of the document @computed get dataDoc() { - return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DocData]; + return this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc ? this.props.TemplateDataDocument ?? this.props.Document[DocData] : this.props.Document[DocData]; } // key where data is stored @computed get fieldKey() { @@ -78,7 +73,7 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() { /// DocAnnotatbleComponent -return a base class for React views of document fields that are annotatable *and* interactive when selected (e.g., pdf, image) export interface ViewBoxAnnotatableProps { Document: Doc; - DataDoc?: Doc; + TemplateDataDocument?: Doc; fieldKey: string; filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example) isContentActive: () => boolean | undefined; @@ -96,17 +91,13 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() @computed get Document() { return this.props.Document; } - // This is the "The Document" -- it encapsulates, data, layout, and any templates - @computed get rootDoc() { - return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; - } // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info @computed get layoutDoc() { return Doc.Layout(this.props.Document); } // This is the data part of a document -- ie, the data that is constant across all views of the document @computed get dataDoc() { - return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DocData]; + return this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc ? this.props.TemplateDataDocument ?? this.props.Document[DocData] : this.props.Document[DocData]; } // key where data is stored diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index bd82f7782..59d7e75da 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -229,7 +229,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV onPointerEnter={action(e => (this.subPin = allDocs ? 'All ' : ''))} onPointerLeave={action(e => (this.subPin = ''))} onClick={e => { - this.props.views().forEach(dv => click(dv!.rootDoc)); + this.props.views().forEach(dv => click(dv!.Document)); e.stopPropagation(); }} /> @@ -356,7 +356,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV const docs = this.props .views() .filter(v => v) - .map(dv => dv!.rootDoc); + .map(dv => dv!.Document); TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: pinLayoutView, @@ -379,7 +379,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV const docs = this.props .views() .filter(v => v) - .map(dv => dv!.rootDoc); + .map(dv => dv!.Document); TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinData: { dataview: e.altKey }, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) }); e.stopPropagation(); }}> @@ -560,9 +560,9 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV @action toggleTrail = (e: React.PointerEvent) => { const rootView = this.props.views()[0]; - const rootDoc = rootView?.rootDoc; - if (rootDoc) { - const anchor = rootView.ComponentView?.getAnchor?.(true) ?? rootDoc; + const doc = rootView?.Document; + if (doc) { + const anchor = rootView.ComponentView?.getAnchor?.(true) ?? doc; const trail = DocCast(anchor.presentationTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true); if (trail !== anchor.presentationTrail) { DocUtils.MakeLink(anchor, trail, { link_relationship: 'link trail' }); @@ -589,9 +589,9 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV <div style={{ position: 'absolute', zIndex: 1000 }}> <LinkPopup key="popup" - linkCreated={link => (link.link_displayLine = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))} + linkCreated={link => (link.link_displayLine = !IsFollowLinkScript(this.props.views().lastElement()?.Document.onClick))} linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.(true)} - linkFrom={() => this.props.views().lastElement()?.rootDoc} + linkFrom={() => this.props.views().lastElement()?.Document} /> </div> ) : ( diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 4ede2e2bb..b9fcf5360 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -116,11 +116,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P SelectionManager.Views().forEach(d => { if (titleFieldKey === 'title') { d.dataDoc.title_custom = !this._accumulatedTitle.startsWith('-'); - if (StrCast(d.rootDoc.title).startsWith('@') && !this._accumulatedTitle.startsWith('@')) { - Doc.RemFromMyPublished(d.rootDoc); + if (StrCast(d.Document.title).startsWith('@') && !this._accumulatedTitle.startsWith('@')) { + Doc.RemFromMyPublished(d.Document); } - if (!StrCast(d.rootDoc.title).startsWith('@') && this._accumulatedTitle.startsWith('@')) { - Doc.AddToMyPublished(d.rootDoc); + if (!StrCast(d.Document.title).startsWith('@') && this._accumulatedTitle.startsWith('@')) { + Doc.AddToMyPublished(d.Document); } } //@ts-ignore @@ -128,20 +128,20 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P if (titleField.toString().startsWith('<this>')) { const title = titleField.toString().replace(/<this>\.?/, ''); - const curKey = Doc.LayoutFieldKey(d.rootDoc); + const curKey = Doc.LayoutFieldKey(d.Document); if (curKey !== title) { if (title) { if (d.dataDoc[title] === undefined || d.dataDoc[title] instanceof RichTextField || typeof d.dataDoc[title] === 'string') { - d.rootDoc.layout_fieldKey = `layout_${title}`; - d.rootDoc[`layout_${title}`] = FormattedTextBox.LayoutString(title); - d.rootDoc[`${title}_nativeWidth`] = d.rootDoc[`${title}_nativeHeight`] = 0; + d.Document.layout_fieldKey = `layout_${title}`; + d.Document[`layout_${title}`] = FormattedTextBox.LayoutString(title); + d.Document[`${title}_nativeWidth`] = d.Document[`${title}_nativeHeight`] = 0; } } else { - d.rootDoc.layout_fieldKey = undefined; + d.Document.layout_fieldKey = undefined; } } } else { - Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true); + Doc.SetInPlace(d.Document, titleFieldKey, titleField, true); } }), 'edit title' @@ -157,7 +157,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P }; onContainerDown = (e: React.PointerEvent) => { - const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views()[0].rootDoc); + const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views()[0].Document); if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) { setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), emptyFunction, emptyFunction); e.stopPropagation(); @@ -165,7 +165,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P }; onTitleDown = (e: React.PointerEvent) => { - const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views()[0].rootDoc); + const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views()[0].Document); if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) { setupMoveUpEvents( this, @@ -189,12 +189,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @action onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => { const dragDocView = SelectionManager.Views()[0]; - const effectiveLayoutAcl = GetEffectiveAcl(dragDocView.rootDoc); + const effectiveLayoutAcl = GetEffectiveAcl(dragDocView.Document); if (effectiveLayoutAcl != AclAdmin && effectiveLayoutAcl != AclEdit && effectiveLayoutAcl != AclAugment) { return false; } const containers = new Set<Doc | undefined>(); - SelectionManager.Views().forEach(v => containers.add(DocCast(v.rootDoc.embedContainer))); + SelectionManager.Views().forEach(v => containers.add(DocCast(v.Document.embedContainer))); if (containers.size > 1) return false; const { left, top } = dragDocView.getBounds() || { left: 0, top: 0 }; const dragData = new DragManager.DocumentDragData( @@ -256,7 +256,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P }; onMaximizeDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, () => DragManager.StartWindowDrag?.(e, [SelectionManager.Views().lastElement().rootDoc]) ?? false, emptyFunction, this.onMaximizeClick, false, false); + setupMoveUpEvents(this, e, () => DragManager.StartWindowDrag?.(e, [SelectionManager.Views().lastElement().Document]) ?? false, emptyFunction, this.onMaximizeClick, false, false); e.stopPropagation(); }; onMaximizeClick = (e: any): void => { @@ -264,19 +264,19 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P if (selectedDocs.length) { if (e.ctrlKey) { // open an embedding in a new tab with Ctrl Key - CollectionDockingView.AddSplit(Doc.BestEmbedding(selectedDocs[0].rootDoc), OpenWhereMod.right); + CollectionDockingView.AddSplit(Doc.BestEmbedding(selectedDocs[0].Document), OpenWhereMod.right); } else if (e.shiftKey) { // open centered in a new workspace with Shift Key - const embedding = Doc.MakeEmbedding(selectedDocs[0].rootDoc); + const embedding = Doc.MakeEmbedding(selectedDocs[0].Document); embedding.embedContainer = undefined; embedding.x = -NumCast(embedding._width) / 2; embedding.y = -NumCast(embedding._height) / 2; CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([embedding], { title: 'Tab for ' + embedding.title }), OpenWhereMod.right); } else if (e.altKey) { // open same document in new tab - CollectionDockingView.ToggleSplit(selectedDocs[0].rootDoc, OpenWhereMod.right); + CollectionDockingView.ToggleSplit(selectedDocs[0].Document, OpenWhereMod.right); } else { - var openDoc = selectedDocs[0].rootDoc; + var openDoc = selectedDocs[0].Document; if (openDoc.layout_fieldKey === 'layout_icon') { openDoc = DocListCast(openDoc.proto_embeddings).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(openDoc); Doc.deiconifyView(openDoc); @@ -284,7 +284,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P LightboxView.Instance.SetLightboxDoc( openDoc, undefined, - selectedDocs.slice(1).map(view => view.rootDoc) + selectedDocs.slice(1).map(view => view.Document) ); } } @@ -346,9 +346,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P setRotateCenter = (seldocview: DocumentView, rotCenter: number[]) => { const newloccentern = seldocview.props.ScreenToLocalTransform().transformPoint(rotCenter[0], rotCenter[1]); const newlocenter = [newloccentern[0] - NumCast(seldocview.layoutDoc._width) / 2, newloccentern[1] - NumCast(seldocview.layoutDoc._height) / 2]; - const final = Utils.rotPt(newlocenter[0], newlocenter[1], -(NumCast(seldocview.rootDoc._rotation) / 180) * Math.PI); - seldocview.rootDoc.rotation_centerX = final.x / NumCast(seldocview.layoutDoc._width); - seldocview.rootDoc.rotation_centerY = final.y / NumCast(seldocview.layoutDoc._height); + const final = Utils.rotPt(newlocenter[0], newlocenter[1], -(NumCast(seldocview.Document._rotation) / 180) * Math.PI); + seldocview.Document.rotation_centerX = final.x / NumCast(seldocview.layoutDoc._width); + seldocview.Document.rotation_centerY = final.y / NumCast(seldocview.layoutDoc._height); }; @action @@ -361,7 +361,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P (e: PointerEvent, down: number[], delta: number[]) => // return false to keep getting events this.setRotateCenter(seldocview, [this.rotCenter[0] + delta[0], this.rotCenter[1] + delta[1]]) as any as boolean, action(e => (this._isRotating = false)), // upEvent - action(e => (seldocview.rootDoc.rotation_centerX = seldocview.rootDoc.rotation_centerY = 0)) + action(e => (seldocview.Document.rotation_centerX = seldocview.Document.rotation_centerY = 0)) ); // prettier-ignore }; @@ -375,22 +375,22 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const infos = new Map<Doc, { unrotatedDocPos: { x: number; y: number }; startRotCtr: { x: number; y: number }; accumRot: number }>(); const seldocview = SelectionManager.Views()[0]; SelectionManager.Views().forEach(dv => { - const accumRot = (NumCast(dv.rootDoc._rotation) / 180) * Math.PI; + const accumRot = (NumCast(dv.Document._rotation) / 180) * Math.PI; const localRotCtr = dv.props.ScreenToLocalTransform().transformPoint(rcScreen.X, rcScreen.Y); - const localRotCtrOffset = [localRotCtr[0] - NumCast(dv.rootDoc.width) / 2, localRotCtr[1] - NumCast(dv.rootDoc.height) / 2]; + const localRotCtrOffset = [localRotCtr[0] - NumCast(dv.Document.width) / 2, localRotCtr[1] - NumCast(dv.Document.height) / 2]; const startRotCtr = Utils.rotPt(localRotCtrOffset[0], localRotCtrOffset[1], -accumRot); - const unrotatedDocPos = { x: NumCast(dv.rootDoc.x) + localRotCtrOffset[0] - startRotCtr.x, y: NumCast(dv.rootDoc.y) + localRotCtrOffset[1] - startRotCtr.y }; - infos.set(dv.rootDoc, { unrotatedDocPos, startRotCtr, accumRot }); + const unrotatedDocPos = { x: NumCast(dv.Document.x) + localRotCtrOffset[0] - startRotCtr.x, y: NumCast(dv.Document.y) + localRotCtrOffset[1] - startRotCtr.y }; + infos.set(dv.Document, { unrotatedDocPos, startRotCtr, accumRot }); }); const infoRot = (angle: number, isAbs = false) => { SelectionManager.Views().forEach( action(dv => { - const { unrotatedDocPos, startRotCtr, accumRot } = infos.get(dv.rootDoc)!; + const { unrotatedDocPos, startRotCtr, accumRot } = infos.get(dv.Document)!; const endRotCtr = Utils.rotPt(startRotCtr.x, startRotCtr.y, isAbs ? angle : accumRot + angle); - infos.set(dv.rootDoc, { unrotatedDocPos, startRotCtr, accumRot: isAbs ? angle : accumRot + angle }); - dv.rootDoc.x = infos.get(dv.rootDoc)!.unrotatedDocPos.x - (endRotCtr.x - startRotCtr.x); - dv.rootDoc.y = infos.get(dv.rootDoc)!.unrotatedDocPos.y - (endRotCtr.y - startRotCtr.y); - dv.rootDoc._rotation = ((isAbs ? 0 : NumCast(dv.rootDoc._rotation)) + (angle * 180) / Math.PI) % 360; // Rotation between -360 and 360 + infos.set(dv.Document, { unrotatedDocPos, startRotCtr, accumRot: isAbs ? angle : accumRot + angle }); + dv.Document.x = infos.get(dv.Document)!.unrotatedDocPos.x - (endRotCtr.x - startRotCtr.x); + dv.Document.y = infos.get(dv.Document)!.unrotatedDocPos.y - (endRotCtr.y - startRotCtr.y); + dv.Document._rotation = ((isAbs ? 0 : NumCast(dv.Document._rotation)) + (angle * 180) / Math.PI) % 360; // Rotation between -360 and 360 }) ); }; @@ -410,7 +410,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P return false; }, // moveEvent action(() => { - const oldRotation = NumCast(seldocview.rootDoc._rotation); + const oldRotation = NumCast(seldocview.Document._rotation); const diff = oldRotation - Math.round(oldRotation / 45) * 45; if (Math.abs(diff) < 5) { if (selectedInk.length) { @@ -459,7 +459,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P }; onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => { const first = SelectionManager.Views()[0]; - const effectiveAcl = GetEffectiveAcl(first.rootDoc); + const effectiveAcl = GetEffectiveAcl(first.Document); if (!(effectiveAcl == AclAdmin || effectiveAcl == AclEdit || effectiveAcl == AclAugment)) return false; if (!first) return false; var fixedAspect = Doc.NativeAspect(first.layoutDoc); @@ -514,7 +514,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P // resize a single DocumentView about the specified reference point, possibly setting/updating the native dimensions of the Doc // resizeView = (docView: DocumentView, refPt: number[], scale: { x: number; y: number }, opts: { dragHdl: string; ctrlKey: boolean }) => { - const doc = docView.rootDoc; + const doc = docView.Document; if (doc.isGroup) { DocListCast(doc.data) .map(member => DocumentManager.Instance.getDocumentView(member, docView)!) @@ -611,11 +611,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P get selectionTitle(): string { if (SelectionManager.Views().length === 1) { const selected = SelectionManager.Views()[0]; - if (selected.ComponentView?.getTitle?.()) { - return selected.ComponentView.getTitle(); - } if (this._titleControlString.startsWith('=')) { - return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ self: selected.rootDoc, this: selected.layoutDoc }, console.log).result?.toString() || ''; + return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ self: selected.Document, this: selected.layoutDoc }, console.log).result?.toString() || ''; } if (this._titleControlString.startsWith('#')) { return Field.toString(selected.props.Document[this._titleControlString.substring(1)] as Field) || '-unset-'; @@ -645,24 +642,24 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } // sharing - const acl = GetEffectiveAcl(!this._showLayoutAcl ? Doc.GetProto(seldocview.rootDoc) : seldocview.rootDoc); + const acl = GetEffectiveAcl(!this._showLayoutAcl ? Doc.GetProto(seldocview.Document) : seldocview.Document); const docShareMode = HierarchyMapping.get(acl)!.name; const shareMode = StrCast(docShareMode); var shareSymbolIcon = ReverseHierarchyMap.get(shareMode)?.image; // hide the decorations if the parent chooses to hide it or if the document itself hides it - const hideDecorations = SnappingManager.GetIsResizing() || seldocview.props.hideDecorations || seldocview.rootDoc.layout_hideDecorations; + const hideDecorations = SnappingManager.GetIsResizing() || seldocview.props.hideDecorations || seldocview.Document.layout_hideDecorations; const hideResizers = - ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating; - const hideTitle = this._showNothing || hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating; - const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.layout_hideDocumentButtonBar || this._isRounding || this._isRotating; + ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(seldocview.Document)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.Document.layout_hideResizeHandles || this._isRounding || this._isRotating; + const hideTitle = this._showNothing || hideDecorations || seldocview.props.hideDecorationTitle || seldocview.Document.layout_hideDecorationTitle || this._isRounding || this._isRotating; + const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.Document.layout_hideDocumentButtonBar || this._isRounding || this._isRotating; // if multiple documents have been opened at the same time, then don't show open button const hideOpenButton = this._showNothing || hideDecorations || seldocview.props.hideOpenButton || - seldocview.rootDoc.layout_hideOpenButton || - SelectionManager.Views().some(docView => docView.rootDoc._dragOnlyWithinContainer || docView.rootDoc.isGroup || docView.rootDoc.layout_hideOpenButton) || + seldocview.Document.layout_hideOpenButton || + SelectionManager.Views().some(docView => docView.Document._dragOnlyWithinContainer || docView.Document.isGroup || docView.Document.layout_hideOpenButton) || this._isRounding || this._isRotating; const hideDeleteButton = @@ -671,10 +668,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P this._isRounding || this._isRotating || seldocview.props.hideDeleteButton || - seldocview.rootDoc.hideDeleteButton || + seldocview.Document.hideDeleteButton || SelectionManager.Views().some(docView => { const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().dataDoc) : AclEdit; - return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin; + return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.Document) !== AclAdmin; }); const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => ( <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top"> @@ -686,13 +683,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const bounds = this.ClippedBounds; const useLock = bounds.r - bounds.x > 135 && seldocview.CollectionFreeFormDocumentView; - const useRotation = !hideResizers && seldocview.rootDoc.type !== DocumentType.EQUATION && seldocview.CollectionFreeFormDocumentView; // when do we want an object to not rotate? + const useRotation = !hideResizers && seldocview.Document.type !== DocumentType.EQUATION && seldocview.CollectionFreeFormDocumentView; // when do we want an object to not rotate? const rotation = SelectionManager.Views().length == 1 ? seldocview.screenToLocalTransform().inverse().RotateDeg : 0; // Radius constants const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView; - const borderRadius = numberValue(Cast(seldocview.rootDoc.layout_borderRounding, 'string', null)); - const docMax = Math.min(NumCast(seldocview.rootDoc.width) / 2, NumCast(seldocview.rootDoc.height) / 2); + const borderRadius = numberValue(Cast(seldocview.Document.layout_borderRounding, 'string', null)); + const docMax = Math.min(NumCast(seldocview.Document.width) / 2, NumCast(seldocview.Document.height) / 2); const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2); const radiusHandle = (borderRadius / docMax) * maxDist; const radiusHandleLocation = Math.min(radiusHandle, maxDist); @@ -740,7 +737,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P {sharingMenu} {!useLock ? null : ( <Tooltip key="lock" title={<div className="dash-tooltip">toggle ability to interact with document</div>} placement="top"> - <div className="documentDecorations-lock" style={{ color: seldocview.rootDoc._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown}> + <div className="documentDecorations-lock" style={{ color: seldocview.Document._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown}> <FontAwesomeIcon size="sm" icon="lock" /> </div> </Tooltip> diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index cb5c9b085..1deca401d 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -21,7 +21,7 @@ import { List } from '../../fields/List'; import { emptyFunction } from '../../Utils'; interface filterProps { - rootDoc: Doc; + Document: Doc; } @observer @@ -34,7 +34,7 @@ export class FilterPanel extends React.Component<filterProps> { * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection */ @computed get targetDoc() { - return this.props.rootDoc; + return this.props.Document; } @computed get targetDocChildKey() { const targetView = DocumentManager.Instance.getFirstDocumentView(this.targetDoc); @@ -113,7 +113,7 @@ export class FilterPanel extends React.Component<filterProps> { // } gatherFieldValues(childDocs: Doc[], facetKey: string) { - const valueSet = new Set<string>(StrListCast(this.props.rootDoc.childFilters).map(filter => filter.split(Doc.FilterSep)[1])); + const valueSet = new Set<string>(StrListCast(this.props.Document.childFilters).map(filter => filter.split(Doc.FilterSep)[1])); let rtFields = 0; let subDocs = childDocs; if (subDocs.length > 0) { @@ -224,7 +224,7 @@ export class FilterPanel extends React.Component<filterProps> { facetValues = (facetHeader: string) => { const allCollectionDocs = new Set<Doc>(); SearchUtil.foreachRecursiveDoc(this.targetDocChildren, (depth: number, doc: Doc) => allCollectionDocs.add(doc)); - const set = new Set<string>([...StrListCast(this.props.rootDoc.childFilters).map(filter => filter.split(Doc.FilterSep)[1]), Doc.FilterNone, Doc.FilterAny]); + const set = new Set<string>([...StrListCast(this.props.Document.childFilters).map(filter => filter.split(Doc.FilterSep)[1]), Doc.FilterNone, Doc.FilterAny]); if (facetHeader === 'tags') allCollectionDocs.forEach(child => StrListCast(child[facetHeader]) diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 0882294a2..559f245d9 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -386,7 +386,7 @@ export class GestureOverlay extends React.Component<React.PropsWithChildren<Gest @computed get elements() { const selView = GestureOverlay.DownDocView; - const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._freeform_scale, 1); // * (selView?.props.ScreenToLocalTransform().Scale || 1); + const width = Number(ActiveInkWidth()) * NumCast(selView?.Document._freeform_scale, 1); // * (selView?.props.ScreenToLocalTransform().Scale || 1); const rect = this._overlayRef.current?.getBoundingClientRect(); const B = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; //this.getBounds(this._points, true); B.left = B.left - width / 2; @@ -466,10 +466,8 @@ export class GestureOverlay extends React.Component<React.PropsWithChildren<Gest this._clipboardDoc = ( <DocumentView Document={doc} - DataDoc={undefined} addDocument={undefined} addDocTab={returnFalse} - rootSelected={returnFalse} pinToPres={emptyFunction} removeDocument={undefined} ScreenToLocalTransform={this.screenToLocalTransform} diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 62165bc48..9efa7baac 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -49,7 +49,7 @@ export class InkStrokeProperties { (strokes instanceof DocumentView ? [strokes] : strokes)?.forEach( action(inkView => { if (!requireCurrPoint || this._currentPoint !== -1) { - const doc = inkView.rootDoc; + const doc = inkView.Document; if (doc.type === DocumentType.INK && doc.width && doc.height) { const ink = Cast(doc.stroke, InkField)?.inkData; if (ink) { @@ -86,7 +86,7 @@ export class InkStrokeProperties { @action addPoints = (inkView: DocumentView, t: number, i: number, controls: { X: number; Y: number }[]) => { this.applyFunction(inkView, (view: DocumentView, ink: InkData) => { - const doc = view.rootDoc; + const doc = view.Document; const array = [controls[i], controls[i + 1], controls[i + 2], controls[i + 3]]; const newsegs = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).split(t); const splicepts = [...newsegs.left.points, ...newsegs.right.points]; @@ -159,7 +159,7 @@ export class InkStrokeProperties { this.applyFunction( inkView, (view: DocumentView, ink: InkData) => { - const doc = view.rootDoc; + const doc = view.Document; const newPoints = ink.slice(); const brokenIndices = NumListCast(doc.brokenInkIndices); if (preserve || this._currentPoint === 0 || this._currentPoint === ink.length - 1 || brokenIndices.includes(this._currentPoint)) { @@ -335,7 +335,7 @@ export class InkStrokeProperties { * Handles the movement/scaling of a control point. */ snapControl = (inkView: DocumentView, controlIndex: number) => { - const inkDoc = inkView.rootDoc; + const inkDoc = inkView.Document; const ink = Cast(inkDoc[Doc.LayoutFieldKey(inkDoc)], InkField)?.inkData; if (ink) { @@ -378,9 +378,9 @@ export class InkStrokeProperties { .filter(doc => doc.type === DocumentType.INK) .forEach(doc => { const testInkView = DocumentManager.Instance.getDocumentView(doc, containingDocView); - const snapped = testInkView?.ComponentView?.snapPt?.(screenDragPt, doc === inkView.rootDoc ? this.excludeSelfSnapSegs(ink, controlIndex) : []); + const snapped = testInkView?.ComponentView?.snapPt?.(screenDragPt, doc === inkView.Document ? this.excludeSelfSnapSegs(ink, controlIndex) : []); if (snapped && snapped.distance < snapData.distance) { - const snappedInkPt = doc === inkView.rootDoc ? snapped.nearestPt : inkView.ComponentView?.ptFromScreen?.(testInkView?.ComponentView?.ptToScreen?.(snapped.nearestPt) ?? { X: 0, Y: 0 }); // convert from snapped ink coordinate system to dragged ink coordinate system by converting to/from screen space + const snappedInkPt = doc === inkView.Document ? snapped.nearestPt : inkView.ComponentView?.ptFromScreen?.(testInkView?.ComponentView?.ptToScreen?.(snapped.nearestPt) ?? { X: 0, Y: 0 }); // convert from snapped ink coordinate system to dragged ink coordinate system by converting to/from screen space if (snappedInkPt) { snapData = { nearestPt: snappedInkPt, distance: snapped.distance }; @@ -397,7 +397,7 @@ export class InkStrokeProperties { */ snapHandleTangent = (inkView: DocumentView, controlIndex: number, handleIndexA: number, handleIndexB: number) => { this.applyFunction(inkView, (view: DocumentView, ink: InkData) => { - const doc = view.rootDoc; + const doc = view.Document; const brokenIndices = Cast(doc.brokenInkIndices, listSpec('number'), []); const ind = brokenIndices.findIndex(value => value === controlIndex); if (ind !== -1) { @@ -459,7 +459,7 @@ export class InkStrokeProperties { @action moveTangentHandle = (inkView: DocumentView, deltaX: number, deltaY: number, handleIndex: number, oppositeHandleIndex: number, controlIndex: number) => this.applyFunction(inkView, (view: DocumentView, ink: InkData) => { - const doc = view.rootDoc; + const doc = view.Document; const closed = InkingStroke.IsClosed(ink); const oldHandlePoint = ink[handleIndex]; const oppositeHandlePoint = ink[oppositeHandleIndex]; diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 98d30b625..de2ce3189 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -247,7 +247,6 @@ export class LightboxView extends React.Component<LightboxViewProps> { <DocumentView ref={action((r: DocumentView | null) => (this._docView = r !== null ? r : undefined))} Document={this._doc} - DataDoc={undefined} PanelWidth={this.lightboxWidth} PanelHeight={this.lightboxHeight} LayoutTemplate={this.lightboxDocTemplate} @@ -256,7 +255,6 @@ export class LightboxView extends React.Component<LightboxViewProps> { styleProvider={DefaultStyleProvider} ScreenToLocalTransform={this.lightboxScreenToLocal} renderDepth={0} - rootSelected={returnFalse} docViewPath={returnEmptyDoclist} childFilters={returnEmptyFilter} childFiltersByRanges={returnEmptyFilter} diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index ac0cc3f8c..58c1570b2 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -599,19 +599,17 @@ export class MainView extends React.Component { <DocumentView key="headerBarDoc" Document={this.headerBarDoc} - DataDoc={undefined} addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={emptyFunction} docViewPath={returnEmptyDoclist} styleProvider={DefaultStyleProvider} - rootSelected={returnFalse} addDocument={this.addHeaderDoc} removeDocument={this.removeHeaderDoc} fitContentsToBox={returnTrue} isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events) isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive ScreenToLocalTransform={this.headerBarScreenXf} - childHideResizeHandles={returnTrue} + childHideResizeHandles={true} childDragAction="move" dontRegisterView={true} hideResizeHandles={true} @@ -635,13 +633,11 @@ export class MainView extends React.Component { <DocumentView key="main" Document={this.mainContainer!} - DataDoc={undefined} addDocument={undefined} addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={emptyFunction} docViewPath={returnEmptyDoclist} styleProvider={this._hideUI ? DefaultStyleProvider : undefined} - rootSelected={returnFalse} isContentActive={returnTrue} removeDocument={undefined} ScreenToLocalTransform={this._hideUI ? this.mainScreenToLocalXf : Transform.Identity} @@ -731,13 +727,11 @@ export class MainView extends React.Component { <div className="mainView-contentArea"> <DocumentView Document={this._sidebarContent.proto || this._sidebarContent} - DataDoc={undefined} addDocument={undefined} addDocTab={DocumentViewInternal.addDocTabFunc} - pinToPres={emptyFunction} + pinToPres={TabDocView.PinDoc} docViewPath={returnEmptyDoclist} - styleProvider={this._sidebarContent.proto === Doc.MyDashboards || this._sidebarContent.proto === Doc.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider} - rootSelected={returnFalse} + styleProvider={this._sidebarContent.proto === Doc.MyDashboards || this._sidebarContent.proto === Doc.MyFilesystem || this._sidebarContent.proto === Doc.MyTrails ? DashboardStyleProvider : DefaultStyleProvider} removeDocument={returnFalse} ScreenToLocalTransform={this.mainContainerXf} PanelWidth={this.leftMenuFlyoutWidth} @@ -762,11 +756,9 @@ export class MainView extends React.Component { <div key="menu" className="mainView-leftMenuPanel" style={{ background: SettingsManager.userBackgroundColor, display: LightboxView.LightboxDoc ? 'none' : undefined }}> <DocumentView Document={Doc.MyLeftSidebarMenu} - DataDoc={undefined} addDocument={undefined} addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={emptyFunction} - rootSelected={returnFalse} removeDocument={returnFalse} ScreenToLocalTransform={this.sidebarScreenToLocal} PanelWidth={this.leftMenuWidth} @@ -901,12 +893,10 @@ export class MainView extends React.Component { <div className="mainView-docButtons" style={{ background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor }} ref={this._docBtnRef}> <CollectionLinearView Document={Doc.MyDockedBtns} - DataDoc={undefined} fieldKey="data" dropAction="embed" setHeight={returnFalse} styleProvider={DefaultStyleProvider} - rootSelected={returnFalse} bringToFront={emptyFunction} select={emptyFunction} isAnyChildContentActive={returnFalse} @@ -988,19 +978,17 @@ export class MainView extends React.Component { select={returnFalse} isSelected={returnFalse} Document={this.headerBarDoc} - DataDoc={undefined} addDocTab={returnFalse} pinToPres={emptyFunction} docViewPath={returnEmptyDoclist} styleProvider={DefaultStyleProvider} - rootSelected={returnFalse} addDocument={returnFalse} removeDocument={returnFalse} fitContentsToBox={returnTrue} isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events) isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive ScreenToLocalTransform={Transform.Identity} - childHideResizeHandles={returnTrue} + childHideResizeHandles={true} childDragAction="move" dontRegisterView={true} PanelWidth={this.headerBarDocWidth} diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 8b8838464..70a44a08a 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -61,7 +61,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> { const savedAnnoMap = savedAnnotations?.values() && Array.from(savedAnnotations?.values()).length ? savedAnnotations : this.props.savedAnnotations(); if (savedAnnoMap.size === 0) return undefined; const savedAnnos = Array.from(savedAnnoMap.values())[0]; - const doc = this.props.docView().rootDoc; + const doc = this.props.docView().Document; const scale = (this.props.annotationLayerScaling?.() || 1) * NumCast(doc._freeform_scale, 1); if (savedAnnos.length && (savedAnnos[0] as any).marqueeing) { const anno = savedAnnos[0]; @@ -185,7 +185,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> { const targetCreator = (annotationOn: Doc | undefined) => { const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow'); - FormattedTextBox.SelectOnLoad = target[Id]; + FormattedTextBox.SetSelectOnLoad(target); return target; }; DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView(), sourceAnchorCreator, targetCreator), e.pageX, e.pageY, { @@ -213,7 +213,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> { dragComplete: e => { if (!e.aborted && e.linkDocument) { Doc.GetProto(e.linkDocument).link_relationship = 'cropped image'; - Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.docView().rootDoc.title; + Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.docView().Document.title; Doc.GetProto(e.linkDocument).link_displayLine = false; } }, @@ -242,7 +242,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> { // configure and show the annotation/link menu if a the drag region is big enough // copy the temporary marquee to allow for multiple selections (not currently available though). const copy = document.createElement('div'); - const scale = (this.props.scaling?.() || 1) * NumCast(this.props.docView().rootDoc._freeform_scale, 1); + const scale = (this.props.scaling?.() || 1) * NumCast(this.props.docView().Document._freeform_scale, 1); ['border', 'opacity', 'top', 'left', 'width', 'height'].forEach(prop => (copy.style[prop as any] = marqueeStyle[prop as any])); copy.className = 'marqueeAnnotator-annotationBox'; copy.style.top = parseInt(marqueeStyle.top.toString().replace('px', '')) / scale + this.props.scrollTop + 'px'; diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index fbcefd460..dd547c549 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -222,7 +222,6 @@ export class OverlayView extends React.Component { style={{ top: d.type === DocumentType.PRES ? 0 : undefined, width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.overlayX}px, ${d.overlayY}px)` }}> <DocumentView Document={d} - rootSelected={returnFalse} bringToFront={emptyFunction} addDocument={undefined} removeDocument={this.removeOverlayDoc} diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index e3a43d45f..d1bd0500b 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -50,7 +50,7 @@ export class PreviewCursor extends React.Component<{}> { PreviewCursor._slowLoadDocuments?.(plain.split('v=')[1].split('&')[0], options, generatedDocuments, '', undefined, PreviewCursor._addDocument).then(batch.end); } else if (re.test(plain)) { const url = plain; - if (url.startsWith(window.location.href)) { + if (!url.startsWith(window.location.href)) { undoBatch(() => PreviewCursor._addDocument( Docs.Create.WebDocument(url, { diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 84b1bf038..6635aabf9 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -40,7 +40,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { @observable public static Instance: PropertiesButtons; @computed get selectedDoc() { - return SelectionManager.SelectedSchemaDoc() || SelectionManager.Views().lastElement()?.rootDoc; + return SelectionManager.SelectedSchemaDoc() || SelectionManager.Views().lastElement()?.Document; } @computed get selectedLayoutDoc() { return SelectionManager.SelectedSchemaDoc() || SelectionManager.Views().lastElement()?.layoutDoc; @@ -65,7 +65,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { toggleType={ToggleType.BUTTON} onClick={undoable(() => { if (SelectionManager.Views().length > 1) { - SelectionManager.Views().forEach(dv => (onClick ?? onPropToggle)(dv, dv.rootDoc, property)); + SelectionManager.Views().forEach(dv => (onClick ?? onPropToggle)(dv, dv.Document, property)); } else if (targetDoc) (onClick ?? onPropToggle)(undefined, targetDoc, property); }, property)} /> @@ -83,7 +83,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { on => 'window-restore', onClick => { SelectionManager.Views().forEach(dv => { - const containerDoc = dv.rootDoc; + const containerDoc = dv.Document; //containerDoc.followAllLinks = // containerDoc.noShadow = // containerDoc.layout_disableBrushing = @@ -91,8 +91,8 @@ export class PropertiesButtons extends React.Component<{}, {}> { //containerDoc._freeform_fitContentsToBox = containerDoc._isLightbox = !containerDoc._isLightbox; //containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined; - const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]); - //dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' }); + const containerContents = DocListCast(dv.dataDoc[Doc.LayoutFieldKey(containerDoc)]); + //dv.Docuemnt.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' }); containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.link_displayLine = false))); }); } @@ -106,7 +106,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { on => 'Switch between title styles', on => (on ? <MdSubtitlesOff /> : <MdSubtitles />), // {currentIcon}, //(on ? <MdSubtitles/> :) , //,'text-width', on ? <MdSubtitles/> : <MdSubtitlesOff/>, (dv, doc) => { - const tdoc = dv?.rootDoc || doc; + const tdoc = dv?.Document || doc; const newtitle = !tdoc._layout_showTitle ? 'title' : tdoc._layout_showTitle === 'title' ? 'title:hover' : ''; tdoc._layout_showTitle = newtitle ? newtitle : undefined; } @@ -201,7 +201,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { // on => 'window-restore', // onClick => { // SelectionManager.Views().forEach(dv => { - // const containerDoc = dv.rootDoc; + // const containerDoc = dv.Document; // //containerDoc.followAllLinks = // // containerDoc.noShadow = // // containerDoc.disableDocBrushing = @@ -210,7 +210,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { // containerDoc._isLightbox = !containerDoc._isLightbox; // //containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined; // const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]); - // //dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' }); + // //dv.Document.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' }); // containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.layout_linkDisplay = false))); // }); // } @@ -236,7 +236,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { '_layout_showCaption', on => `${on ? 'Hide' : 'Show'} caption footer`, on => (on ? <MdClosedCaptionDisabled /> : <MdClosedCaption />), //'closed-captioning', - (dv, doc) => ((dv?.rootDoc || doc)._layout_showCaption = (dv?.rootDoc || doc)._layout_showCaption === undefined ? 'caption' : undefined) + (dv, doc) => ((dv?.Document || doc)._layout_showCaption = (dv?.Document || doc)._layout_showCaption === undefined ? 'caption' : undefined) ); } @@ -247,7 +247,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { '_chromeHidden', on => `${on ? 'Show' : 'Hide'} editing UI`, on => (on ? <TbEditCircle /> : <TbEditCircleOff />), // 'edit', - (dv, doc) => ((dv?.rootDoc || doc)._chromeHidden = !(dv?.rootDoc || doc)._chromeHidden) + (dv, doc) => ((dv?.Document || doc)._chromeHidden = !(dv?.Document || doc)._chromeHidden) ); } @@ -443,7 +443,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { @undoBatch editOnClickScript = () => { - if (SelectionManager.Views().length) SelectionManager.Views().forEach(dv => DocUtils.makeCustomViewClicked(dv.rootDoc, undefined, 'onClick')); + if (SelectionManager.Views().length) SelectionManager.Views().forEach(dv => DocUtils.makeCustomViewClicked(dv.Document, undefined, 'onClick')); else this.selectedDoc && DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, 'onClick'); }; diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx index d157e7b1c..196250167 100644 --- a/src/client/views/PropertiesDocContextSelector.tsx +++ b/src/client/views/PropertiesDocContextSelector.tsx @@ -21,7 +21,7 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC @computed get _docs() { if (!this.props.DocView) return []; const target = this.props.DocView.props.Document; - const targetContext = this.props.DocView.props.docViewPath().lastElement()?.rootDoc; + const targetContext = this.props.DocView.props.docViewPath().lastElement()?.Document; const embeddings = DocListCast(target.proto_embeddings); const containerProtos = embeddings.filter(embedding => embedding.embedContainer && embedding.embedContainer instanceof Doc).reduce((set, embedding) => set.add(Cast(embedding.embedContainer, Doc, null)), new Set<Doc>()); const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.proto_embeddings)); diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 76ea9123d..c5b0528af 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -293,10 +293,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { <div ref={this.propertiesDocViewRef} style={{ pointerEvents: 'none', display: 'inline-block', height: panelHeight() }} key={this.selectedDoc[Id]}> <DocumentView Document={this.selectedDoc} - DataDoc={this.dataDoc} + TemplateDataDocument={Doc.AreProtosEqual(this.dataDoc, this.selectedDoc) ? undefined : this.dataDoc} renderDepth={1} fitContentsToBox={returnTrue} - rootSelected={returnFalse} styleProvider={DefaultStyleProvider} docViewPath={returnEmptyDoclist} dontCenter={'y'} @@ -450,7 +449,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { */ @computed get sharingTable() { // all selected docs - const docs = SelectionManager.Views().length < 2 && this.selectedDoc ? [this.selectedDoc] : SelectionManager.Views().map(docView => docView.rootDoc); + const docs = SelectionManager.Views().length < 2 && this.selectedDoc ? [this.selectedDoc] : SelectionManager.Views().map(docView => docView.Document); const target = docs[0]; const showAdmin = GetEffectiveAcl(target) == AclAdmin; @@ -566,7 +565,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { @computed get editableTitle() { const titles = new Set<string>(); - SelectionManager.Views().forEach(dv => titles.add(StrCast(dv.rootDoc.title))); + SelectionManager.Views().forEach(dv => titles.add(StrCast(dv.Document.title))); const title = Array.from(titles.keys()).length > 1 ? '--multiple selected--' : StrCast(this.selectedDoc?.title); return ( <div> @@ -623,7 +622,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { @action setTitle = (value: string | number) => { if (SelectionManager.Views().length > 1) { - SelectionManager.Views().map(dv => Doc.SetInPlace(dv.rootDoc, 'title', value, true)); + SelectionManager.Views().map(dv => Doc.SetInPlace(dv.Document, 'title', value, true)); } else if (this.dataDoc) { if (this.selectedDoc) Doc.SetInPlace(this.selectedDoc, 'title', value, true); else KeyValueBox.SetField(this.dataDoc, 'title', value as string, true); @@ -1173,7 +1172,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { return ( <PropertiesSection title="Filters" isOpen={this.openFilters} setIsOpen={bool => (this.openFilters = bool)} onDoubleClick={() => this.CloseAll()}> <div className="propertiesView-content filters" style={{ position: 'relative', height: 'auto' }}> - <FilterPanel rootDoc={this.selectedDoc ?? Doc.ActiveDashboard!} /> + <FilterPanel Document={this.selectedDoc ?? Doc.ActiveDashboard!} /> </div> </PropertiesSection> ); diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 1e1b8e0e6..049ba4841 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -20,8 +20,8 @@ import React = require('react'); interface ExtraProps { fieldKey: string; + Document: Doc; layoutDoc: Doc; - rootDoc: Doc; dataDoc: Doc; // usePanelWidth: boolean; showSidebar: boolean; @@ -42,7 +42,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { _stackRef = React.createRef<CollectionStackingView>(); @computed get allMetadata() { const keys = new Map<string, FieldResult<Field>>(); - DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => + DocListCast(this.props.Document[this.sidebarKey]).forEach(doc => SearchUtil.documentKeys(doc) .filter(key => key[0] && key[0] !== '_' && key[0] === key[0].toUpperCase()) .map(key => keys.set(key, doc[key])) @@ -51,7 +51,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { } @computed get allHashtags() { const keys = new Set<string>(); - DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => StrListCast(doc.tags).forEach(tag => keys.add(tag))); + DocListCast(this.props.Document[this.sidebarKey]).forEach(doc => StrListCast(doc.tags).forEach(tag => keys.add(tag))); return Array.from(keys.keys()) .filter(key => key[0]) .filter(key => !key.startsWith('_') && (key[0] === '#' || key[0] === key[0].toUpperCase())) @@ -59,7 +59,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { } @computed get allUsers() { const keys = new Set<string>(); - DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author))); + DocListCast(this.props.Document[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author))); return Array.from(keys.keys()).sort(); } @@ -69,7 +69,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { .join(' '); const target = Docs.Create.TextDocument(startup, { title: '-note-', - annotationOn: this.props.rootDoc, + annotationOn: this.props.Document, _width: 200, _height: 50, _layout_fitWidth: true, @@ -77,7 +77,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { _text_fontSize: StrCast(Doc.UserDoc().fontSize), _text_fontFamily: StrCast(Doc.UserDoc().fontFamily), }); - FormattedTextBox.SelectOnLoad = target[Id]; + FormattedTextBox.SetSelectOnLoad(target); FormattedTextBox.DontSelectInitialText = true; const link = DocUtils.MakeLink(anchor, target, { link_relationship: 'inline comment:comment on' }); link && (link.link_displayLine = false); @@ -147,7 +147,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { return target; }; makeDocUnfiltered = (doc: Doc) => { - if (DocListCast(this.props.rootDoc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) { + if (DocListCast(this.props.Document[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) { if (this.childFilters()) { // if any child filters exist, get rid of them this.props.layoutDoc._childFilters = new List<string>(); @@ -188,7 +188,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { const renderTag = (tag: string) => { const active = this.childFilters().includes(`tags${Doc.FilterSep}${tag}${Doc.FilterSep}check`); return ( - <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.rootDoc, 'tags', tag, 'check', true, undefined, e.shiftKey)}> + <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.Document, 'tags', tag, 'check', true, undefined, e.shiftKey)}> {tag} </div> ); @@ -196,7 +196,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { const renderMeta = (tag: string, dflt: FieldResult<Field>) => { const active = this.childFilters().includes(`${tag}${Doc.FilterSep}${Doc.FilterAny}${Doc.FilterSep}exists`); return ( - <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.rootDoc, tag, Doc.FilterAny, 'exists', true, undefined, e.shiftKey)}> + <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.Document, tag, Doc.FilterAny, 'exists', true, undefined, e.shiftKey)}> {tag} </div> ); @@ -204,7 +204,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { const renderUsers = (user: string) => { const active = this.childFilters().includes(`author:${user}:check`); return ( - <div key={user} className={`sidebarAnnos-filterUser${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.rootDoc, 'author', user, 'check', true, undefined, e.shiftKey)}> + <div key={user} className={`sidebarAnnos-filterUser${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.Document, 'author', user, 'check', true, undefined, e.shiftKey)}> {user} </div> ); @@ -215,9 +215,9 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { style={{ position: 'absolute', pointerEvents: this.props.isContentActive() ? 'all' : undefined, - top: this.props.rootDoc.type !== DocumentType.RTF && StrCast(this.props.rootDoc._layout_showTitle) === 'title' ? 15 : 0, + top: this.props.Document.type !== DocumentType.RTF && StrCast(this.props.Document._layout_showTitle) === 'title' ? 15 : 0, right: 0, - background: this.props.styleProvider?.(this.props.rootDoc, this.props, StyleProp.WidgetColor), + background: this.props.styleProvider?.(this.props.Document, this.props, StyleProp.WidgetColor), width: `100%`, height: '100%', }}> @@ -248,7 +248,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { isAnyChildContentActive={returnFalse} childDocumentsActive={this.props.isContentActive} whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} - childHideDecorationTitle={returnTrue} + childHideDecorationTitle={true} removeDocument={this.removeDocument} moveDocument={this.moveDocument} addDocument={this.addDocument} diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 5399d38b4..806c9c001 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -64,6 +64,9 @@ function toggleLockedPosition(doc: Doc) { export function testDocProps(toBeDetermined: any): toBeDetermined is DocumentViewProps { return toBeDetermined?.isContentActive ? toBeDetermined : undefined; } +export function testFieldProps(toBeDetermined: any): toBeDetermined is FieldViewProps { + return toBeDetermined?.isContentActive ? toBeDetermined : undefined; +} export function wavyBorderPath(pw: number, ph: number, inset: number = 0.05) { return `M ${pw * 0.5} ${ph * inset} C ${pw * 0.6} ${ph * inset} ${pw * (1 - 2 * inset)} 0 ${pw * (1 - inset)} ${ph * inset} C ${pw} ${ph * (2 * inset)} ${pw * (1 - inset)} ${ph * 0.25} ${pw * (1 - inset)} ${ph * 0.3} C ${ @@ -77,19 +80,20 @@ export function wavyBorderPath(pw: number, ph: number, inset: number = 0.05) { // a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab // -export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any { +export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps | FieldViewProps>, property: string): any { const remoteDocHeader = 'author;author_date;noMargin'; const docProps = testDocProps(props) ? props : undefined; + const fieldProps = testFieldProps(props) ? props : undefined; const selected = property.includes(':selected'); const isCaption = property.includes(':caption'); const isAnchor = property.includes(':anchor'); const isContent = property.includes(':content'); const isAnnotated = property.includes(':annotated'); - const isInk = () => doc?._layout_isSvg && !props?.LayoutTemplateString; + const isInk = () => doc?._layout_isSvg && !docProps?.LayoutTemplateString; const isOpen = property.includes(':open'); const isEmpty = property.includes(':empty'); const boxBackground = property.includes(':box'); - const fieldKey = props?.fieldKey ? props.fieldKey + '_' : isCaption ? 'caption_' : ''; + const fieldKey = fieldProps?.fieldKey ? fieldProps.fieldKey + '_' : isCaption ? 'caption_' : ''; const lockedPosition = () => doc && BoolCast(doc._lockedPosition); const titleHeight = () => props?.styleProvider?.(doc, props, StyleProp.TitleHeight); const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor); @@ -132,20 +136,21 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps return undefined; case StyleProp.DocContents:return undefined; case StyleProp.WidgetColor:return isAnnotated ? Colors.LIGHT_BLUE : 'dimgrey'; - case StyleProp.Opacity: return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : doc?.text_inlineAnnotations ? 0 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null)); + case StyleProp.Opacity: return docProps?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : doc?.text_inlineAnnotations ? 0 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null)); case StyleProp.HideLinkBtn:return props?.hideLinkButton || (!selected && doc?.layout_hideLinkButton); case StyleProp.FontSize: return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(doc?._text_fontSize, StrCast(Doc.UserDoc().fontSize))); case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(doc?._text_fontFamily, StrCast(Doc.UserDoc().fontFamily))); case StyleProp.FontWeight: return StrCast(doc?.[fieldKey + 'fontWeight'], StrCast(doc?._text_fontWeight, StrCast(Doc.UserDoc().fontWeight))); case StyleProp.FillColor: return StrCast(doc?._fillColor, StrCast(doc?.fillColor, 'transparent')); case StyleProp.ShowCaption:return doc?._type_collection === CollectionViewType.Carousel || props?.hideCaptions ? undefined : StrCast(doc?._layout_showCaption); - case StyleProp.TitleHeight:return (props?.ScreenToLocalTransform().Scale ?? 1)*(props?.NativeDimScaling?.()??1) * NumCast(Doc.UserDoc().headerHeight,30) + case StyleProp.TitleHeight: + return (props?.ScreenToLocalTransform().Scale ?? 1)*(props?.NativeDimScaling?.()??1) * NumCast(Doc.UserDoc().headerHeight,30) case StyleProp.ShowTitle: return ( (doc && - !props?.LayoutTemplateString && + !docProps?.LayoutTemplateString && !doc.presentation_targetDoc && - !props?.LayoutTemplateString?.includes(KeyValueBox.name) && + !docProps?.LayoutTemplateString?.includes(KeyValueBox.name) && props?.layout_showTitle?.() !== '' && StrCast( doc._layout_showTitle, @@ -249,7 +254,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps default: return doc.z ? `#9c9396 ${StrCast(doc?.layout_boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow - : props?.docViewPath().lastElement()?.rootDoc._freeform_useClusters + : props?.docViewPath().lastElement()?.Document._freeform_useClusters ? `${backgroundCol()} ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent : NumCast(doc.group, -1) !== -1 ? `gray ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent @@ -259,8 +264,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps } } case StyleProp.PointerEvents: - if (StrCast(doc?.pointerEvents) && !props?.LayoutTemplateString?.includes(KeyValueBox.name)) return StrCast(doc!.pointerEvents); // honor pointerEvents field (set by lock button usually) if it's not a keyValue view of the Doc - if (docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.() !== undefined) return docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.(); + if (StrCast(doc?.pointerEvents) && !docProps?.LayoutTemplateString?.includes(KeyValueBox.name)) return StrCast(doc!.pointerEvents); // honor pointerEvents field (set by lock button usually) if it's not a keyValue view of the Doc + if (docProps?.DocumentView?.().props.LayoutTemplateString?.includes(KeyValueBox.name)) return 'all'; if (DocumentView.ExploreMode || doc?.layout_unrendered) return isInk() ? 'visiblePainted' : 'all'; if (props?.pointerEvents?.() === 'none') return 'none'; if (opacity() === 0) return 'none'; @@ -270,7 +275,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps return undefined; // fixes problem with tree view elements getting pointer events when the tree view is not active case StyleProp.Decorations: const lock = () => { - if (props?.docViewPath().lastElement()?.rootDoc?._type_collection === CollectionViewType.Freeform) { + if (props?.docViewPath().lastElement()?.Document?._type_collection === CollectionViewType.Freeform) { return doc?.pointerEvents !== 'none' ? null : ( <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}> <FontAwesomeIcon icon='lock' size="lg" /> @@ -315,9 +320,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps color={SettingsManager.userColor} background={showFilterIcon} items={[ ...(dashView ? [dashView]: []), ...(props?.docViewPath?.()??[]), ...(props?.DocumentView?[props?.DocumentView?.()]:[])] - .filter(dv => StrListCast(dv.rootDoc.childFilters).length || StrListCast(dv.rootDoc.childRangeFilters).length) + .filter(dv => StrListCast(dv.Document.childFilters).length || StrListCast(dv.Document.childRangeFilters).length) .map(dv => ({ - text: StrCast(dv.rootDoc.title), + text: StrCast(dv.Document.title), val: dv as any, style: {color:SettingsManager.userColor, background:SettingsManager.userBackgroundColor}, } as IListItemProps)) } @@ -365,7 +370,7 @@ export function DashboardToggleButton(doc: Doc, field: string, onIcon: IconProp, ); } /** - * add lock and hide button decorations for the "Dashboards" flyout TreeView + * add hide button decorations for the "Dashboards" flyout TreeView */ export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) { if (doc && property.split(':')[0] === StyleProp.Decorations) { diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index a3884c9eb..6da228417 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -119,7 +119,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> { childFilters={returnEmptyFilter} childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - rootSelected={returnFalse} onCheckedClick={this.scriptField} onChildClick={this.scriptField} isAnyChildContentActive={returnFalse} diff --git a/src/client/views/collections/CollectionCarousel3DView.scss b/src/client/views/collections/CollectionCarousel3DView.scss index 8319f19ca..1632d44db 100644 --- a/src/client/views/collections/CollectionCarousel3DView.scss +++ b/src/client/views/collections/CollectionCarousel3DView.scss @@ -3,6 +3,7 @@ height: 100%; position: relative; background-color: white; + overflow: hidden; } .carousel-wrapper { diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index a8d080953..d6368464a 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -2,16 +2,16 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; +import { Utils, returnFalse, returnZero } from '../../../Utils'; import { Doc, DocListCast } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; -import { returnFalse, returnZero, Utils } from '../../../Utils'; import { DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; import { SelectionManager } from '../../util/SelectionManager'; +import { StyleProp } from '../StyleProvider'; import { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } from '../global/globalCssVariables.scss'; import { DocFocusOptions, DocumentView } from '../nodes/DocumentView'; -import { StyleProp } from '../StyleProvider'; import './CollectionCarousel3DView.scss'; import { CollectionSubView } from './CollectionSubView'; @@ -61,15 +61,16 @@ export class CollectionCarousel3DView extends CollectionSubView() { return ( <DocumentView {...this.props} + Document={childPair.layout} + TemplateDataDocument={childPair.data} + //suppressSetHeight={true} NativeWidth={returnZero} NativeHeight={returnZero} - //suppressSetHeight={true} + layout_fitWidth={undefined} onDoubleClick={this.onChildDoubleClick} renderDepth={this.props.renderDepth + 1} LayoutTemplate={this.props.childLayoutTemplate} LayoutTemplateString={this.props.childLayoutString} - Document={childPair.layout} - DataDoc={childPair.data} focus={this.focus} ScreenToLocalTransform={this.childScreenToLocal} isContentActive={this.isChildContentActive} diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 33a92d406..299a4d5d3 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -2,13 +2,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; +import { StopEvent, emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils'; import { Doc, Opt } from '../../../fields/Doc'; -import { NumCast, ScriptCast, StrCast } from '../../../fields/Types'; -import { emptyFunction, returnFalse, returnOne, returnZero, StopEvent } from '../../../Utils'; +import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { DragManager } from '../../util/DragManager'; +import { StyleProp } from '../StyleProvider'; import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; -import { StyleProp } from '../StyleProvider'; import './CollectionCarouselView.scss'; import { CollectionSubView } from './CollectionSubView'; @@ -51,7 +51,7 @@ export class CollectionCarouselView extends CollectionSubView() { const index = NumCast(this.layoutDoc._carousel_index); const curDoc = this.childLayoutPairs?.[index]; const captionProps = { ...this.props, NativeScaling: returnOne, PanelWidth: this.captionWidth, fieldKey: 'caption', setHeight: undefined, setContentView: undefined }; - const show_captions = StrCast(this.layoutDoc._layout_showCaption); + const carouselShowsCaptions = StrCast(this.layoutDoc._layout_showCaption); return !(curDoc?.layout instanceof Doc) ? null : ( <> <div className="collectionCarouselView-image" key="image"> @@ -59,34 +59,36 @@ export class CollectionCarouselView extends CollectionSubView() { {...this.props} NativeWidth={returnZero} NativeHeight={returnZero} + layout_fitWidth={undefined} setContentView={undefined} onDoubleClick={this.onContentDoubleClick} onClick={this.onContentClick} isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.props.isContentActive} isContentActive={this.props.childContentsActive ?? this.props.isContentActive() === false ? returnFalse : emptyFunction} - hideCaptions={show_captions ? true : false} + hideCaptions={!!carouselShowsCaptions} // hide captions if the carousel is configured to show the captions renderDepth={this.props.renderDepth + 1} LayoutTemplate={this.props.childLayoutTemplate} LayoutTemplateString={this.props.childLayoutString} Document={curDoc.layout} - DataDoc={curDoc.layout.resolvedDataDoc as Doc} + TemplateDataDocument={DocCast(curDoc.layout.resolvedDataDoc)} PanelHeight={this.panelHeight} bringToFront={returnFalse} /> </div> - <div - className="collectionCarouselView-caption" - key="caption" - onWheel={StopEvent} - style={{ - display: show_captions ? undefined : 'none', - borderRadius: this.props.styleProvider?.(this.layoutDoc, captionProps, StyleProp.BorderRounding), - marginRight: this.marginX, - marginLeft: this.marginX, - width: `calc(100% - ${this.marginX * 2}px)`, - }}> - <FormattedTextBox key={index} {...captionProps} fieldKey={show_captions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} DataDoc={undefined} /> - </div> + {!carouselShowsCaptions ? null : ( + <div + className="collectionCarouselView-caption" + key="caption" + onWheel={StopEvent} + style={{ + borderRadius: this.props.styleProvider?.(this.layoutDoc, captionProps, StyleProp.BorderRounding), + marginRight: this.marginX, + marginLeft: this.marginX, + width: `calc(100% - ${this.marginX * 2}px)`, + }}> + <FormattedTextBox key={index} {...captionProps} fieldKey={carouselShowsCaptions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} TemplateDataDocument={undefined} /> + </div> + )} </> ); } diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 06522b85e..a9b5f401d 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -155,7 +155,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr const key = this.props.pivotField; const newDoc = Docs.Create.TextDocument('', { _layout_autoHeight: true, _width: 200, _layout_fitWidth: true, title: value }); const onLayoutDoc = this.onLayoutDoc(key); - FormattedTextBox.SelectOnLoad = newDoc[Id]; + FormattedTextBox.SetSelectOnLoad(newDoc); FormattedTextBox.SelectOnLoadChar = value; (onLayoutDoc ? newDoc : newDoc[DocData])[key] = this.getValue(this.props.heading); const docs = this.props.parent.childDocList; diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 3c555e731..8b3531da8 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -97,12 +97,10 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> { <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this.props.panelHeight() }}> <CollectionLinearView Document={selDoc} - DataDoc={undefined} fieldKey="data" dropAction="embed" setHeight={returnFalse} styleProvider={DefaultStyleProvider} - rootSelected={returnFalse} bringToFront={emptyFunction} select={emptyFunction} isContentActive={returnTrue} diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index c7424e7e5..ad5431c8e 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -221,7 +221,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { blockPointerEventsWhenDragging = () => (this.docsDraggedRowCol.length ? 'none' : undefined); // getDisplayDoc returns the rules for displaying a document in this view (ie. DocumentView) getDisplayDoc(doc: Doc, width: () => number) { - const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.DataDoc; + const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.TemplateDataDocument; const height = () => this.getDocHeight(doc); let dref: Opt<DocumentView>; const noteTakingDocTransform = () => this.getDocTransform(doc, dref); @@ -229,7 +229,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { <DocumentView ref={r => (dref = r || undefined)} Document={doc} - DataDoc={dataDoc ?? (!Doc.AreProtosEqual(doc[DocData], doc) ? doc[DocData] : undefined)} + TemplateDataDocument={dataDoc ?? (!Doc.AreProtosEqual(doc[DocData], doc) ? doc[DocData] : undefined)} pointerEvents={this.blockPointerEventsWhenDragging} renderDepth={this.props.renderDepth + 1} PanelWidth={width} @@ -257,9 +257,8 @@ export class CollectionNoteTakingView extends CollectionSubView() { ScreenToLocalTransform={noteTakingDocTransform} focus={this.focusDocument} childFilters={this.childDocFilters} - hideDecorationTitle={this.props.childHideDecorationTitle?.()} - hideResizeHandles={this.props.childHideResizeHandles?.()} - hideTitle={this.props.childHideTitle?.()} + hideDecorationTitle={this.props.childHideDecorationTitle} + hideResizeHandles={this.props.childHideResizeHandles} childFiltersByRanges={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} addDocument={this.props.addDocument} @@ -297,7 +296,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { getDocHeight(d?: Doc) { if (!d || d.hidden) return 0; const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.()); - const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc; + const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.TemplateDataDocument; const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1)); const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? NumCast(d._width) : 0); const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? NumCast(d._height) : 0); @@ -336,7 +335,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { // onPointerMove is used to preview where a document will drop in a column once a drag is complete. @action onPointerMove = (force: boolean, ex: number, ey: number) => { - if (this.childDocList?.includes(DragManager.DocDragData?.draggedDocuments?.lastElement() as any) || force || this.isContentActive()) { + if (this.childDocList?.includes(DragManager.DocDragData?.draggedDocuments?.lastElement() as any) || force || SnappingManager.GetCanEmbed()) { // get the current docs for the column based on the mouse's x coordinate const xCoord = this.props.ScreenToLocalTransform().transformPoint(ex, ey)[0] - 2 * this.gridGap; const colDocs = this.getDocsFromXCoord(xCoord); @@ -407,11 +406,11 @@ export class CollectionNoteTakingView extends CollectionSubView() { @action onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => { const docView = fieldProps.DocumentView?.(); - if (docView && (e.ctrlKey || docView.rootDoc._createDocOnCR) && ['Enter'].includes(e.key)) { + if (docView && (e.ctrlKey || docView.Document._createDocOnCR) && ['Enter'].includes(e.key)) { e.stopPropagation?.(); - const newDoc = Doc.MakeCopy(docView.rootDoc, true); + const newDoc = Doc.MakeCopy(docView.Document, true); Doc.GetProto(newDoc).text = undefined; - FormattedTextBox.SelectOnLoad = newDoc[Id]; + FormattedTextBox.SetSelectOnLoad(newDoc); return this.addDocument?.(newDoc); } }; @@ -526,7 +525,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { chromeHidden={this.chromeHidden} colHeaderData={this.colHeaderData} Document={this.props.Document} - DataDoc={this.props.DataDoc} + TemplateDataDocument={this.props.TemplateDataDocument} resizeColumns={this.resizeColumns} renderChildren={this.children} numGroupColumns={this.numGroupColumns} diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx index 52cc21903..9e5f09479 100644 --- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx +++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx @@ -25,7 +25,7 @@ import './CollectionNoteTakingView.scss'; interface CSVFieldColumnProps { Document: Doc; - DataDoc: Opt<Doc>; + TemplateDataDocument: Opt<Doc>; docList: Doc[]; heading: string; pivotField: string; @@ -134,7 +134,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _layout_fitWidth: true, title: value, _layout_autoHeight: true }); const colValue = this.getValue(this.props.heading); newDoc[key] = colValue; - FormattedTextBox.SelectOnLoad = newDoc[Id]; + FormattedTextBox.SetSelectOnLoad(newDoc); FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' '; return this.props.addDocument?.(newDoc) || false; }; @@ -157,14 +157,14 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu ContextMenu.Instance.clearItems(); const layoutItems: ContextMenuProps[] = []; const docItems: ContextMenuProps[] = []; - const dataDoc = this.props.DataDoc || this.props.Document; + const dataDoc = this.props.TemplateDataDocument || this.props.Document; const pivotValue = this.getValue(this.props.heading); DocUtils.addDocumentCreatorMenuItems( doc => { const key = this.props.pivotField; doc[key] = this.getValue(this.props.heading); - FormattedTextBox.SelectOnLoad = doc[Id]; + FormattedTextBox.SetSelectOnLoad(doc); return this.props.addDocument?.(doc); }, this.props.addDocument, diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 7bde2cb5f..7fee1cda6 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -17,7 +17,6 @@ import { DragManager } from '../../util/DragManager'; import { FollowLinkScript, IsFollowLinkScript, LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; -import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from '../../util/UndoManager'; @@ -35,6 +34,7 @@ export type CollectionStackedTimelineProps = { playLink: (linkDoc: Doc, options: DocFocusOptions) => void; playFrom: (seekTimeInSeconds: number, endTime?: number) => void; playing: () => boolean; + thumbnails?: () => string[]; setTime: (time: number) => void; startTag: string; endTag: string; @@ -43,7 +43,6 @@ export type CollectionStackedTimelineProps = { rawDuration: number; dataFieldKey: string; fieldKey: string; - thumbnails?: () => string[]; }; // trimming state: shows full clip, current trim bounds, or not trimming @@ -395,7 +394,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack const anchor = docAnchor ?? Docs.Create.LabelDocument({ - title: ComputedField.MakeFunction(`self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`) as any, + title: ComputedField.MakeFunction(`this["${endTag}"] ? "#" + formatToTime(this["${startTag}"]) + "-" + formatToTime(this["${endTag}"]) : "#" + formatToTime(this["${startTag}"])`) as any, _label_minFontSize: 12, _label_maxFontSize: 24, _dragOnlyWithinContainer: true, @@ -799,7 +798,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> NativeHeight={returnZero} ref={action((r: DocumentView | null) => (anchor.view = r))} Document={mark} - DataDoc={undefined} + TemplateDataDocument={undefined} docViewPath={returnEmptyDoclist} pointerEvents={this.noEvents ? returnNone : undefined} styleProvider={this.props.styleProvider} @@ -817,7 +816,6 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> searchFilterDocs={returnEmptyDoclist} childFilters={returnEmptyFilter} childFiltersByRanges={returnEmptyFilter} - rootSelected={returnFalse} onClick={script} onDoubleClick={this.props.layoutDoc.autoPlayAnchors ? undefined : doublescript} ignoreAutoHeight={false} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 57ff7515e..59e0c60b9 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -285,14 +285,14 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection e.stopPropagation?.(); const below = !e.altKey && e.key !== 'Tab'; const layout_fieldKey = StrCast(docView.LayoutFieldKey); - const newDoc = Doc.MakeCopy(docView.rootDoc, true); - const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)]; + const newDoc = Doc.MakeCopy(docView.Document, true); + const dataField = docView.Document[Doc.LayoutFieldKey(newDoc)]; newDoc[DocData][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined; - if (layout_fieldKey !== 'layout' && docView.rootDoc[layout_fieldKey] instanceof Doc) { - newDoc[layout_fieldKey] = docView.rootDoc[layout_fieldKey]; + if (layout_fieldKey !== 'layout' && docView.Document[layout_fieldKey] instanceof Doc) { + newDoc[layout_fieldKey] = docView.Document[layout_fieldKey]; } Doc.GetProto(newDoc).text = undefined; - FormattedTextBox.SelectOnLoad = newDoc[Id]; + FormattedTextBox.SetSelectOnLoad(newDoc); return this.addDocument?.(newDoc); } }; @@ -313,7 +313,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection // this is what renders the document that you see on the screen // called in Children: this actually adds a document to our children list getDisplayDoc(doc: Doc, width: () => number, count: number) { - const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.DataDoc; + const dataDoc = doc.isTemplateDoc || doc.isTemplateForField ? this.props.TemplateDataDocument : undefined; const height = () => this.getDocHeight(doc); const panelHeight = () => (this.isStackingView ? height() : Math.min(height(), this.props.PanelHeight())); const panelWidth = () => (this.isStackingView ? width() : this.columnWidth); @@ -323,7 +323,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection <DocumentView ref={action((r: DocumentView) => r?.ContentDiv && this.docRefs.set(doc, r))} Document={doc} - DataDoc={dataDoc ?? (!Doc.AreProtosEqual(doc[DocData], doc) ? doc[DocData] : undefined)} + TemplateDataDocument={dataDoc ?? (Doc.AreProtosEqual(doc[DocData], doc) ? undefined : doc[DocData])} renderDepth={this.props.renderDepth + 1} PanelWidth={panelWidth} PanelHeight={panelHeight} @@ -349,9 +349,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection ScreenToLocalTransform={stackedDocTransform} focus={this.focusDocument} childFilters={this.childDocFilters} - hideDecorationTitle={this.props.childHideDecorationTitle?.()} - hideResizeHandles={this.props.childHideResizeHandles?.()} - hideTitle={this.props.childHideTitle?.()} + hideDecorationTitle={this.props.childHideDecorationTitle} + hideResizeHandles={this.props.childHideResizeHandles} childFiltersByRanges={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} xPadding={NumCast(this.layoutDoc._childXPadding, this.props.childXPadding)} @@ -387,7 +386,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection getDocHeight(d?: Doc) { if (!d || d.hidden) return 0; const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.()); - const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc; + const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.TemplateDataDocument; const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1)); const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!this.childFitWidth(childLayoutDoc) ? NumCast(d._width) : 0); const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!this.childFitWidth(childLayoutDoc) ? NumCast(d._height) : 0); @@ -558,7 +557,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection chromeHidden={this.chromeHidden} colHeaderData={this.colHeaderData} Document={this.props.Document} - DataDoc={this.props.DataDoc} + TemplateDataDocument={this.props.TemplateDataDocument} renderChildren={this.children} columnWidth={this.columnWidth} numGroupColumns={this.numGroupColumns} @@ -678,7 +677,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection addDocTab={this.props.addDocTab} onBrowseClick={this.props.onBrowseClick} pinToPres={emptyFunction} - rootSelected={this.props.isSelected} + rootSelected={this.rootSelected} removeDocument={this.props.removeDocument} ScreenToLocalTransform={Transform.Identity} PanelWidth={this.return35} diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 3598d548a..9bd412fb8 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -26,7 +26,7 @@ import { Id } from '../../../fields/FieldSymbols'; // So this is how we are storing a column interface CSVFieldColumnProps { Document: Doc; - DataDoc: Opt<Doc>; + TemplateDataDocument: Opt<Doc>; docList: Doc[]; heading: string; pivotField: string; @@ -137,7 +137,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC const maxHeading = this.props.docList.reduce((maxHeading, doc) => (NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading), 0); const heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3; newDoc.heading = heading; - FormattedTextBox.SelectOnLoad = newDoc[Id]; + FormattedTextBox.SetSelectOnLoad(newDoc); FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' '; return this.props.addDocument?.(newDoc) || false; }; @@ -211,12 +211,12 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC ContextMenu.Instance.clearItems(); const layoutItems: ContextMenuProps[] = []; const docItems: ContextMenuProps[] = []; - const dataDoc = this.props.DataDoc || this.props.Document; + const dataDoc = this.props.TemplateDataDocument || this.props.Document; const width = this._ele ? Number(getComputedStyle(this._ele).width.replace('px', '')) : 0; const height = this._ele ? Number(getComputedStyle(this._ele).height.replace('px', '')) : 0; DocUtils.addDocumentCreatorMenuItems( doc => { - FormattedTextBox.SelectOnLoad = doc[Id]; + FormattedTextBox.SetSelectOnLoad(doc); return this.props.addDocument?.(doc); }, this.props.addDocument, diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 8896e2d61..f082ca0da 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -6,7 +6,7 @@ import { AclPrivate } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; -import { Cast, ScriptCast, StrCast } from '../../../fields/Types'; +import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types'; import { WebField } from '../../../fields/URLField'; import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; @@ -20,7 +20,7 @@ import { ImageUtils } from '../../util/Import & Export/ImageUtils'; import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch, UndoManager } from '../../util/UndoManager'; -import { DocComponent } from '../DocComponent'; +import { ViewBoxBaseComponent } from '../DocComponent'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { LoadingBox } from '../nodes/LoadingBox'; import { CollectionView, CollectionViewProps } from './CollectionView'; @@ -31,7 +31,7 @@ export interface SubCollectionViewProps extends CollectionViewProps { } export function CollectionSubView<X>(moreProps?: X) { - class CollectionSubView extends DocComponent<X & SubCollectionViewProps>() { + class CollectionSubView extends ViewBoxBaseComponent<X & SubCollectionViewProps>() { private dropDisposer?: DragManager.DragDropDisposer; private gestureDisposer?: GestureUtils.GestureEventDisposer; protected _mainCont?: HTMLDivElement; @@ -56,14 +56,18 @@ export function CollectionSubView<X>(moreProps?: X) { } @computed get dataDoc() { - return this.props.DataDoc instanceof Doc && this.props.Document.isTemplateForField ? Doc.GetProto(this.props.DataDoc) : this.props.Document.resolvedDataDoc ? this.props.Document : Doc.GetProto(this.props.Document); // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template + return this.props.TemplateDataDocument instanceof Doc && this.props.Document.isTemplateForField + ? Doc.GetProto(this.props.TemplateDataDocument) + : this.props.Document.resolvedDataDoc + ? this.props.Document + : Doc.GetProto(this.props.Document); // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template } // this returns whether either the collection is selected, or the template that it is part of is selected - rootSelected = () => this.props.isSelected() || this.props.rootSelected(); + rootSelected = () => this.props.isSelected() || BoolCast(this.props.TemplateDataDocument && this.props.rootSelected?.()); - // The data field for rendering this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.DataDoc. - // When a document has a DataDoc but it's not a template, then it contains its own rendering data, but needs to pass the DataDoc through + // The data field for rendering this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.TemplateDataDocument. + // When a document has a TemplateDataDoc but it's not a template, then it contains its own rendering data, but needs to pass the TemplateDataDoc through // to its children which may be templates. // If 'annotationField' is specified, then all children exist on that field of the extension document, otherwise, they exist directly on the data document under 'fieldKey' @computed get dataField() { @@ -71,9 +75,9 @@ export function CollectionSubView<X>(moreProps?: X) { } @computed get childLayoutPairs(): { layout: Doc; data: Doc }[] { - const { Document, DataDoc } = this.props; + const { Document, TemplateDataDocument } = this.props; const validPairs = this.childDocs - .map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.isAnnotationOverlay ? DataDoc : undefined, doc)) + .map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.isAnnotationOverlay ? TemplateDataDocument : undefined, doc)) .filter(pair => { // filter out any documents that have a proto that we don't have permissions to return !pair.layout?.hidden && pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate)); @@ -104,8 +108,8 @@ export function CollectionSubView<X>(moreProps?: X) { // Finally, if it's not a doc or a list and the document is a template, we try to render the root doc. // For example, if an image doc is rendered with a slide template, the template will try to render the data field as a collection. // Since the data field is actually an image, we set the list of documents to the singleton of root document's proto which will be an image. - const rootDoc = Cast(this.props.Document.rootDocument, Doc, null); - rawdocs = rootDoc && !this.props.isAnnotationOverlay ? [Doc.GetProto(rootDoc)] : []; + const templateRoot = this.props.TemplateDataDocument; + rawdocs = templateRoot && !this.props.isAnnotationOverlay ? [Doc.GetProto(templateRoot)] : []; } const childDocs = rawdocs.filter(d => !(d instanceof Promise) && GetEffectiveAcl(Doc.GetProto(d)) !== AclPrivate && (this.props.ignoreUnrendered || !d.layout_unrendered)).map(d => d as Doc); diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 1cdd200e5..8d114761a 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -35,7 +35,7 @@ export class CollectionTimeView extends CollectionSubView() { async componentDidMount() { this.props.setContentView?.(this); runInAction(() => { - this._childClickedScript = ScriptField.MakeScript('openInLightbox(self)', { this: Doc.name }); + this._childClickedScript = ScriptField.MakeScript('openInLightbox(this)', { this: Doc.name }); this._viewDefDivClick = ScriptField.MakeScript('pivotColumnClick(this,payload)', { payload: 'any' }); }); } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index b34598731..1315decb2 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -63,7 +63,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree return this.props.Document; } @computed get dataDoc() { - return this.props.DataDoc || this.doc; + return this.props.TemplateDataDocument || this.doc; } @computed get treeViewtruncateTitleWidth() { return NumCast(this.doc.treeView_TruncateTitleWidth, this.panelWidth()); @@ -166,7 +166,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree const prev = ind && DocListCast(targetDataDoc[this.props.fieldKey])[ind - 1]; this.props.removeDocument?.(doc); if (ind > 0 && prev) { - FormattedTextBox.SelectOnLoad = prev[Id]; + FormattedTextBox.SetSelectOnLoad(prev); DocumentManager.Instance.getDocumentView(prev, this.props.DocumentView?.())?.select(false); } return true; @@ -236,7 +236,6 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree renderDepth={this.props.renderDepth + 1} isContentActive={this.isContentActive} isDocumentActive={this.isContentActive} - rootSelected={returnFalse} forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not layout_autoHeight PanelWidth={this.documentTitleWidth} PanelHeight={this.documentTitleHeight} @@ -272,7 +271,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree this, this, this.doc, - this.props.DataDoc, + this.props.TemplateDataDocument, undefined, undefined, addDoc, @@ -327,15 +326,15 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree <div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}> <DocumentView Document={menuDoc} - DataDoc={menuDoc} + TemplateDataDocument={menuDoc} isContentActive={this.props.isContentActive} isDocumentActive={returnTrue} addDocument={this.props.addDocument} moveDocument={this.props.moveDocument} removeDocument={this.props.removeDocument} addDocTab={this.props.addDocTab} - pinToPres={emptyFunction} - rootSelected={this.props.isSelected} + pinToPres={this.props.pinToPres} + rootSelected={this.rootSelected} ScreenToLocalTransform={Transform.Identity} PanelWidth={this.return35} PanelHeight={this.return35} diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 389a9a534..2e4c5af6b 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -11,7 +11,6 @@ import { DocUtils } from '../../documents/Documents'; import { CollectionViewType } from '../../documents/DocumentTypes'; import { dropActionType } from '../../util/DragManager'; import { ImageUtils } from '../../util/Import & Export/ImageUtils'; -import { InteractionUtils } from '../../util/InteractionUtils'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; @@ -50,10 +49,9 @@ interface CollectionViewProps_ extends FieldViewProps { childlayout_showTitle?: () => string; childOpacity?: () => number; childContextMenuItems?: () => { script: ScriptField; label: string }[]; - childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children - childHideDecorationTitle?: () => boolean; - childHideResizeHandles?: () => boolean; childLayoutTemplate?: () => Doc | undefined; // specify a layout Doc template to use for children of the collection + childHideDecorationTitle?: boolean; + childHideResizeHandles?: boolean; childDragAction?: dropActionType; childXPadding?: number; childYPadding?: number; @@ -141,7 +139,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab }; setupViewTypes(category: string, func: (type_collection: CollectionViewType) => Doc) { - if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._type_collection !== CollectionViewType.Docking && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) { + if (!Doc.IsSystem(this.Document) && this.Document._type_collection !== CollectionViewType.Docking && !this.dataDoc.isGroup && !this.Document.annotationOn) { // prettier-ignore const subItems: ContextMenuProps[] = [ { description: 'Freeform', event: () => func(CollectionViewType.Freeform), icon: 'signature' }, @@ -172,7 +170,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 !Doc.noviceMode && this.setupViewTypes('Appearance...', vtype => { - const newRendition = Doc.MakeEmbedding(this.rootDoc); + const newRendition = Doc.MakeEmbedding(this.Document); newRendition._type_collection = vtype; this.props.addDocTab(newRendition, OpenWhere.addRight); return newRendition; @@ -180,18 +178,18 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab const options = cm.findByDescription('Options...'); const optionItems = options && 'subitems' in options ? options.subitems : []; - !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? 'Select' : 'Force'} Contents Active`, event: () => (this.rootDoc.forceActive = !this.rootDoc.forceActive), icon: 'project-diagram' }) : null; - if (this.rootDoc.childLayout instanceof Doc) { - optionItems.push({ description: 'View Child Layout', event: () => this.props.addDocTab(this.rootDoc.childLayout as Doc, OpenWhere.addRight), icon: 'project-diagram' }); + !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.Document.forceActive ? 'Select' : 'Force'} Contents Active`, event: () => (this.Document.forceActive = !this.Document.forceActive), icon: 'project-diagram' }) : null; + if (this.Document.childLayout instanceof Doc) { + optionItems.push({ description: 'View Child Layout', event: () => this.props.addDocTab(this.Document.childLayout as Doc, OpenWhere.addRight), icon: 'project-diagram' }); } - if (this.rootDoc.childClickedOpenTemplateView instanceof Doc) { - optionItems.push({ description: 'View Child Detailed Layout', event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, OpenWhere.addRight), icon: 'project-diagram' }); + if (this.Document.childClickedOpenTemplateView instanceof Doc) { + optionItems.push({ description: 'View Child Detailed Layout', event: () => this.props.addDocTab(this.Document.childClickedOpenTemplateView as Doc, OpenWhere.addRight), icon: 'project-diagram' }); } - !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc._isLightbox ? 'Unset' : 'Set'} is Lightbox`, event: () => (this.rootDoc._isLightbox = !this.rootDoc._isLightbox), icon: 'project-diagram' }); + !Doc.noviceMode && optionItems.push({ description: `${this.layoutDoc._isLightbox ? 'Unset' : 'Set'} is Lightbox`, event: () => (this.layoutDoc._isLightbox = !this.layoutDoc._isLightbox), icon: 'project-diagram' }); !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'hand-point-right' }); - if (!Doc.noviceMode && !this.rootDoc.annotationOn) { + if (!Doc.noviceMode && !this.Document.annotationOn) { const existingOnClick = cm.findByDescription('OnClick...'); const onClicks = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : []; const funcs = [ @@ -203,7 +201,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab description: `Edit ${func.name} script`, icon: 'edit', event: (obj: any) => { - const embedding = Doc.MakeEmbedding(this.rootDoc); + const embedding = Doc.MakeEmbedding(this.Document); DocUtils.makeCustomViewClicked(embedding, undefined, func.key); this.props.addDocTab(embedding, OpenWhere.addRight); }, @@ -213,16 +211,16 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab onClicks.push({ description: `Set child ${childClick.title}`, icon: 'edit', - event: () => (Doc.GetProto(this.rootDoc)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data))), + event: () => (this.dataDoc[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data))), }) ); - !Doc.IsSystem(this.rootDoc) && !existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' }); + !Doc.IsSystem(this.Document) && !existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' }); } if (!Doc.noviceMode) { const more = cm.findByDescription('More...'); const moreItems = more && 'subitems' in more ? more.subitems : []; - moreItems.push({ description: 'Export Image Hierarchy', icon: 'columns', event: () => ImageUtils.ExportHierarchyToFileSystem(this.rootDoc) }); + moreItems.push({ description: 'Export Image Hierarchy', icon: 'columns', event: () => ImageUtils.ExportHierarchyToFileSystem(this.Document) }); !more && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'hand-point-right' }); } } @@ -230,9 +228,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab bodyPanelWidth = () => this.props.PanelWidth(); - childHideResizeHandles = () => this.props.childHideResizeHandles?.() ?? BoolCast(this.Document.childHideResizeHandles); - childHideDecorationTitle = () => this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle); - childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null); + childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.Document.childLayoutTemplate, Doc, null); isContentActive = (outsideReaction?: boolean) => this._isContentActive; render() { @@ -244,20 +240,20 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab removeDocument: this.removeDocument, isContentActive: this.isContentActive, isAnyChildContentActive: this.isAnyChildContentActive, - whenChildContentsActiveChanged: this.whenChildContentsActiveChanged, PanelWidth: this.bodyPanelWidth, PanelHeight: this.props.PanelHeight, ScreenToLocalTransform: this.screenToLocalTransform, childLayoutTemplate: this.childLayoutTemplate, - childLayoutString: StrCast(this.rootDoc.childLayoutString, this.props.childLayoutString), - childHideResizeHandles: this.childHideResizeHandles, - childHideDecorationTitle: this.childHideDecorationTitle, + whenChildContentsActiveChanged: this.whenChildContentsActiveChanged, + childLayoutString: StrCast(this.Document.childLayoutString, this.props.childLayoutString), + childHideResizeHandles: this.props.childHideResizeHandles ?? BoolCast(this.Document.childHideResizeHandles), + childHideDecorationTitle: this.props.childHideDecorationTitle ?? BoolCast(this.Document.childHideDecorationTitle), }; return ( <div className="collectionView" onContextMenu={this.onContextMenu} - style={{ pointerEvents: this.props.DocumentView?.()?.props.docViewPath().lastElement()?.rootDoc?._type_collection === CollectionViewType.Freeform && this.rootDoc._lockedPosition ? 'none' : undefined }}> + style={{ pointerEvents: this.props.DocumentView?.()?.props.docViewPath().lastElement()?.Document?._type_collection === CollectionViewType.Freeform && this.layoutDoc._lockedPosition ? 'none' : undefined }}> {this.renderSubView(this.collectionViewType, props)} </div> ); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 4f3c5b9a2..def4beca3 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -35,6 +35,7 @@ import { CollectionView } from './CollectionView'; import './TabDocView.scss'; import React = require('react'); import { Docs } from '../../documents/Documents'; +import { ComputedField } from '../../../fields/ScriptField'; const _global = (window /* browser */ || global) /* node */ as any; interface TabDocViewProps { @@ -261,9 +262,9 @@ export class TabDocView extends React.Component<TabDocViewProps> { } const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.(false, pinProps); const pinDoc = anchorDoc?.type === DocumentType.CONFIG ? anchorDoc : Docs.Create.ConfigDocument({}); - pinDoc.presentation_targetDoc = anchorDoc ?? doc; + const targDoc = (pinDoc.presentation_targetDoc = anchorDoc ?? doc); pinDoc.title = doc.title + ' - Slide'; - pinDoc.data = new List<Doc>(); // the children of the embedding's layout are the presentation slide children. the embedding's data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data + pinDoc.data = targDoc.type === DocumentType.PRES ? ComputedField.MakeFunction('copyField(this.presentation_targetDoc.data') : new List<Doc>(); // the children of the embedding's layout are the presentation slide children. the embedding's data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data pinDoc.presentation_movement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom; pinDoc.presentation_duration = pinDoc.presentation_duration ?? 1000; pinDoc.presentation_groupWithUp = false; @@ -377,9 +378,9 @@ export class TabDocView extends React.Component<TabDocViewProps> { case undefined: case OpenWhere.lightbox: if (this.layoutDoc?._isLightbox) { const lightboxView = !doc.annotationOn && DocCast(doc.embedContainer) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.embedContainer)) : undefined; - const data = lightboxView?.dataDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)]; + const data = lightboxView?.dataDoc[Doc.LayoutFieldKey(lightboxView.Document)]; if (lightboxView && (!data || data instanceof List)) { - lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)] = new List<Doc>([doc]); + lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.Document)] = new List<Doc>([doc]); return true; } } @@ -443,7 +444,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { LayoutTemplateString={this.props.keyValue ? KeyValueBox.LayoutString() : undefined} hideTitle={this.props.keyValue} Document={this._document} - DataDoc={!Doc.AreProtosEqual(this._document[DocData], this._document) ? this._document[DocData] : undefined} + TemplateDataDocument={!Doc.AreProtosEqual(this._document[DocData], this._document) ? this._document[DocData] : undefined} onBrowseClick={DocumentView.exploreMode} waitForDoubleClickToClick={this.waitForDoubleClick} isContentActive={this.isContentActive} @@ -459,7 +460,6 @@ export class TabDocView extends React.Component<TabDocViewProps> { addDocTab={this.addDocTab} ScreenToLocalTransform={this.ScreenToLocalTransform} dontCenter={'y'} - rootSelected={returnFalse} whenChildContentsActiveChanged={this.whenChildContentActiveChanges} focus={this.focusFunc} docViewPath={returnEmptyDoclist} @@ -598,7 +598,6 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> { dontRegisterView={true} fieldKey={Doc.LayoutFieldKey(this.props.document)} bringToFront={emptyFunction} - rootSelected={returnFalse} addDocument={returnFalse} moveDocument={returnFalse} removeDocument={returnFalse} diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 9de74b761..0de1068f7 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -199,10 +199,10 @@ export class TreeView extends React.Component<TreeViewProps> { this._disposers.selection?.(); if (!docView) { this._editTitle = false; - } else if (docView.isSelected()) { + } else if (docView.SELECTED) { this._editTitle = true; this._disposers.selection = reaction( - () => docView.isSelected(), + () => docView.SELECTED, isSel => !isSel && this.setEditTitle(undefined) ); } else { @@ -215,7 +215,7 @@ export class TreeView extends React.Component<TreeViewProps> { this.treeViewOpen = !this.treeViewOpen; } else { // choose an appropriate embedding or make one. --- choose the first embedding that (1) user owns, (2) has no context field ... otherwise make a new embedding - const bestEmbedding = docView.rootDoc.author === Doc.CurrentUserEmail && !Doc.IsDataProto(docView.props.Document) ? docView.rootDoc : Doc.BestEmbedding(docView.rootDoc); + const bestEmbedding = docView.Document.author === Doc.CurrentUserEmail && !Doc.IsDataProto(docView.props.Document) ? docView.Document : Doc.BestEmbedding(docView.Document); this.props.addDocTab(bestEmbedding, OpenWhere.lightbox); } }; @@ -824,9 +824,9 @@ export class TreeView extends React.Component<TreeViewProps> { contextMenuItems = () => { const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'New Folder' }; const folderOp = this.childDocs?.length ? [makeFolder] : []; - const openEmbedding = { script: ScriptField.MakeFunction(`openDoc(getEmbedding(self), "${OpenWhere.addRight}")`)!, icon: 'copy', label: 'Open New Embedding' }; - const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Focus or Open' }; - const reopenDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Reopen' }; + const openEmbedding = { script: ScriptField.MakeFunction(`openDoc(getEmbedding(this), "${OpenWhere.addRight}")`)!, icon: 'copy', label: 'Open New Embedding' }; + const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(this)`)!, icon: 'eye', label: 'Focus or Open' }; + const reopenDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(this)`)!, icon: 'eye', label: 'Reopen' }; return [ ...(this.props.contextMenuItems ?? []).filter(mi => (!mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result)), ...(this.doc.isFolder @@ -849,7 +849,7 @@ export class TreeView extends React.Component<TreeViewProps> { return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label })); }; - onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!); + onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(this)`)!); onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeView_ChildDoubleClick, !this.props.treeView.outlineMode ? this._openScript?.() : null); @@ -964,7 +964,6 @@ export class TreeView extends React.Component<TreeViewProps> { } })} Document={this.doc} - DataDoc={undefined} // or this.dataDoc? layout_fitWidth={returnTrue} scriptContext={this} hideDecorationTitle={this.props.treeView.outlineMode} @@ -975,17 +974,15 @@ export class TreeView extends React.Component<TreeViewProps> { treeViewDoc={this.props.treeView.props.Document} addDocument={undefined} addDocTab={this.props.addDocTab} - rootSelected={returnFalse} - pinToPres={emptyFunction} + pinToPres={this.props.treeView.props.pinToPres} onClick={this.onChildClick} onDoubleClick={this.onChildDoubleClick} dragAction={this.props.dragAction} moveDocument={this.move} removeDocument={this.props.removeDoc} ScreenToLocalTransform={this.getTransform} - NativeHeight={return18} + NativeHeight={returnZero} NativeWidth={returnZero} - shouldNotScale={returnTrue} PanelWidth={this.titleWidth} PanelHeight={return18} contextMenuItems={this.contextMenuItems} @@ -1060,7 +1057,6 @@ export class TreeView extends React.Component<TreeViewProps> { key={this.doc[Id]} ref={action((r: DocumentView | null) => (this._dref = r))} Document={this.doc} - DataDoc={undefined} layout_fitWidth={this.fitWidthFilter} PanelWidth={this.embeddedPanelWidth} PanelHeight={this.embeddedPanelHeight} @@ -1070,7 +1066,7 @@ export class TreeView extends React.Component<TreeViewProps> { isDocumentActive={isActive} styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider} hideTitle={asText} - //fitContentsToBox={returnTrue} + fitContentsToBox={returnTrue} hideDecorationTitle={this.props.treeView.outlineMode} hideResizeHandles={this.props.treeView.outlineMode} onClick={this.onChildClick} @@ -1081,7 +1077,6 @@ export class TreeView extends React.Component<TreeViewProps> { ScreenToLocalTransform={this.docTransform} renderDepth={this.props.renderDepth + 1} treeViewDoc={this.props.treeView?.props.Document} - rootSelected={returnFalse} docViewPath={this.props.treeView.props.docViewPath} childFilters={returnEmptyFilter} childFiltersByRanges={returnEmptyFilter} @@ -1251,7 +1246,7 @@ export class TreeView extends React.Component<TreeViewProps> { const fieldKey = Doc.LayoutFieldKey(newParent); if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) { remove(child); - FormattedTextBox.SelectOnLoad = child[Id]; + FormattedTextBox.SetSelectOnLoad(child); TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined; Doc.AddDocToList(newParent, fieldKey, child, addAfter, false); newParent.treeView_Open = true; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx index dde803ab5..bb7cb0461 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx @@ -25,12 +25,12 @@ export class CollectionFreeFormInfoUI extends React.Component<CollectionFreeForm this._disposers.reaction1 = reaction( () => this.props.Freeform.childDocs.slice(), docs => { - if ((docs.length = 1)) { + if (docs.length === 1) { this.firstDoc = docs[0]; this.firstDocPos = { x: NumCast(this.firstDoc.x), y: NumCast(this.firstDoc.y) }; this.message = 'Doc 1'; } - if ((docs.length = 2)) { + if (docs.length === 2) { console.log('hello 1', docs[0]); console.log('hello 2', docs[1]); this.message = 'Doc 2'; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index aca6df3c9..0c0d45a30 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -37,11 +37,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo this._anchorDisposer = reaction( () => [ this.props.A.props.ScreenToLocalTransform(), - Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop, - Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.[DocCss], + Cast(Cast(Cast(this.props.A.Document, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop, + Cast(Cast(Cast(this.props.A.Document, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.[DocCss], this.props.B.props.ScreenToLocalTransform(), - Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop, - Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.[DocCss], + Cast(Cast(Cast(this.props.A.Document, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop, + Cast(Cast(Cast(this.props.A.Document, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.[DocCss], ], action(() => { this._start = Date.now(); @@ -223,8 +223,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo const pt2normlen = Math.sqrt(pt2norm[0] * pt2norm[0] + pt2norm[1] * pt2norm[1]) || 1; const pt1normalized = [pt1norm[0] / pt1normlen, pt1norm[1] / pt1normlen]; const pt2normalized = [pt2norm[0] / pt2normlen, pt2norm[1] / pt2normlen]; - const aActive = A.isSelected() || A.rootDoc[Brushed]; - const bActive = B.isSelected() || B.rootDoc[Brushed]; + const aActive = A.SELECTED || A.Document[Brushed]; + const bActive = B.SELECTED || B.Document[Brushed]; const textX = (Math.min(pt1[0], pt2[0]) + Math.max(pt1[0], pt2[0])) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetX); const textY = (pt1[1] + pt2[1]) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetY); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 499e0d284..e432c9682 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -123,12 +123,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection return renderableEles; } @computed get fitToContentVals() { + const hgt = this.contentBounds.b - this.contentBounds.y; + const wid = this.contentBounds.r - this.contentBounds.x; return { - bounds: { ...this.contentBounds, cx: (this.contentBounds.x + this.contentBounds.r) / 2, cy: (this.contentBounds.y + this.contentBounds.b) / 2 }, + bounds: { ...this.contentBounds, cx: this.contentBounds.x + wid / 2, cy: this.contentBounds.y + hgt / 2 }, scale: - !this.childDocs.length || !Number.isFinite(this.contentBounds.b - this.contentBounds.y) || !Number.isFinite(this.contentBounds.r - this.contentBounds.x) - ? 1 - : Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)), + (!this.childDocs.length || !Number.isFinite(hgt) || !Number.isFinite(wid) + ? 1 // + : Math.min(this.props.PanelHeight() / hgt, this.props.PanelWidth() / wid)) / (this.props.NativeDimScaling?.() || 1), }; } @computed get fitContentsToBox() { @@ -145,19 +147,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection ); } @computed get nativeWidth() { - return this.props.NativeWidth?.() || (this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null))); + return this.props.NativeWidth?.() || Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); } @computed get nativeHeight() { - return this.props.NativeHeight?.() || (this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null))); + return this.props.NativeHeight?.() || Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); } @computed get cachedCenteringShiftX(): number { - const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling; + const scaling = !this.nativeDimScaling ? 1 : this.nativeDimScaling; return this.props.isAnnotationOverlay || this.props.originTopLeft ? 0 : this.props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections } @computed get cachedCenteringShiftY(): number { const dv = this.props.DocumentView?.(); const fitWidth = this.props.layout_fitWidth?.(this.Document) ?? dv?.layoutDoc.layout_fitWidth; - const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling; + const scaling = !this.nativeDimScaling ? 1 : this.nativeDimScaling; // if freeform has a native aspect, then the panel height needs to be adjusted to match it const aspect = dv?.nativeWidth && dv?.nativeHeight && !fitWidth ? dv.nativeHeight / dv.nativeWidth : this.props.PanelHeight() / this.props.PanelWidth(); return this.props.isAnnotationOverlay || this.props.originTopLeft ? 0 : (aspect * this.props.PanelWidth()) / 2 / scaling; // shift so pan position is at center of window for non-overlay collections @@ -222,14 +224,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); elementFunc = () => this._layoutElements; fitContentOnce = () => { - if (this.props.DocumentView?.().nativeWidth) return; const vals = this.fitToContentVals; this.layoutDoc._freeform_panX = vals.bounds.cx; this.layoutDoc._freeform_panY = vals.bounds.cy; this.layoutDoc._freeform_scale = vals.scale; }; freeformData = (force?: boolean) => (!this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined); - ignoreNativeDimScaling = () => (this.fitContentsToBox ? true : false); // freeform_panx, freeform_pany, freeform_scale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document. // this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panX, 1)); @@ -240,9 +240,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection ScreenToLocalXf = () => this.screenToLocalXf.copy(); getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout); isAnyChildContentActive = () => this.props.isAnyChildContentActive(); - addLiveTextBox = (newBox: Doc) => { - FormattedTextBox.SelectOnLoad = newBox[Id]; // track the new text box so we can give it a prop that tells it to focus itself when it's displayed - this.addDocument(newBox); + addLiveTextBox = (newDoc: Doc) => { + FormattedTextBox.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed + this.addDocument(newDoc); }; selectDocuments = (docs: Doc[]) => { SelectionManager.DeselectAll(); @@ -1178,7 +1178,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection newDoc[layout_fieldKey] = docView.Document[layout_fieldKey]; } Doc.GetProto(newDoc).text = undefined; - FormattedTextBox.SelectOnLoad = newDoc[Id]; + FormattedTextBox.SetSelectOnLoad(newDoc); return this.addDocument?.(newDoc); } }; @@ -1205,8 +1205,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection <CollectionFreeFormDocumentViewWrapper {...OmitKeys(entry, ['replica', 'pair']).omit} key={childLayout[Id] + (entry.replica || '')} - DataDoc={childData} Document={childLayout} + TemplateDataDocument={childData} dragStarting={this.dragStarting} dragEnding={this.dragEnding} isGroupActive={this.props.isGroupActive} @@ -1245,7 +1245,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection layout_showTitle={this.props.childlayout_showTitle} dontRegisterView={this.props.dontRegisterView} pointerEvents={this.childPointerEventsFunc} - //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.treeView_FreezeChildDimensions)} // bcz: check this /> ); } @@ -1312,8 +1311,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection width: _width, height: _height, transition: StrCast(childDocLayout.dataTransition), - pair, pointerEvents: Cast(childDoc.pointerEvents, 'string', null), + pair, replica: '', }; } @@ -1795,7 +1794,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection brushedView={this.brushedView} isAnnotationOverlay={this.isAnnotationOverlay} transform={this.PanZoomCenterXf} - transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.props.DocumentView?.()?.rootDoc._viewTransition, 'string', null))} + transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.props.DocumentView?.()?.Document._viewTransition, 'string', null))} viewDefDivClick={this.props.viewDefDivClick}> {this.underlayViews} {this.contentViews} @@ -1893,7 +1892,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection <DocumentView {...this.props} Document={this._lightboxDoc} - DataDoc={undefined} + TemplateDataDocument={undefined} PanelWidth={this.lightboxPanelWidth} PanelHeight={this.lightboxPanelHeight} NativeWidth={returnZero} @@ -1936,7 +1935,7 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY const browseTransitionTime = 500; SelectionManager.DeselectAll(); dv && - DocumentManager.Instance.showDocument(dv.rootDoc, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => { + DocumentManager.Instance.showDocument(dv.Document, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => { if (!focused) { const selfFfview = !dv.Document.isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined; let containers = dv.props.docViewPath(); @@ -1965,19 +1964,19 @@ ScriptingGlobals.add(function curKeyFrame(readOnly: boolean) { }); ScriptingGlobals.add(function pinWithView(pinContent: boolean) { SelectionManager.Views().forEach(view => - view.props.pinToPres(view.rootDoc, { - currentFrame: Cast(view.rootDoc.currentFrame, 'number', null), + view.props.pinToPres(view.Document, { + currentFrame: Cast(view.Document.currentFrame, 'number', null), pinData: { poslayoutview: pinContent, dataview: pinContent, }, - pinViewport: MarqueeView.CurViewBounds(view.rootDoc, view.props.PanelWidth(), view.props.PanelHeight()), + pinViewport: MarqueeView.CurViewBounds(view.Document, view.props.PanelWidth(), view.props.PanelHeight()), }) ); }); ScriptingGlobals.add(function bringToFront() { - SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.rootDoc)); + SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document)); }); ScriptingGlobals.add(function sendToBack(doc: Doc) { - SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.rootDoc, true)); + SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document, true)); }); diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index be806d3cc..8daed9746 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -187,12 +187,11 @@ export class CollectionGridView extends CollectionSubView() { return ( <DocumentView {...this.props} - shouldNotScale={returnFalse} NativeWidth={returnZero} NativeHeight={returnZero} setContentView={emptyFunction} Document={layout} - DataDoc={layout.resolvedDataDoc as Doc} + TemplateDataDocument={layout.resolvedDataDoc as Doc} isContentActive={this.isChildContentActive} PanelWidth={width} PanelHeight={height} @@ -351,7 +350,7 @@ export class CollectionGridView extends CollectionSubView() { undoBatch( action(() => { const text = Docs.Create.TextDocument('', { _width: 150, _height: 50 }); - FormattedTextBox.SelectOnLoad = text[Id]; // track the new text box so we can give it a prop that tells it to focus itself when it's displayed + FormattedTextBox.SetSelectOnLoad(text); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed Doc.AddDocToList(this.props.Document, this.props.fieldKey, text); this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(e.clientX, e.clientY)))); }) diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index a06ed3a2d..a367e3e73 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -126,8 +126,8 @@ export class CollectionLinearView extends CollectionSubView() { Currently playing: {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => ( <> - <span className="audio-title" onPointerDown={() => DocumentManager.Instance.showDocument(clip.rootDoc, { willZoomCentered: true })}> - {clip.rootDoc.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? ' ' : ',')} + <span className="audio-title" onPointerDown={() => DocumentManager.Instance.showDocument(clip.Document, { willZoomCentered: true })}> + {clip.Document.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? ' ' : ',')} </span> <FontAwesomeIcon icon={!clip.ComponentView?.IsPlaying?.() ? 'play' : 'pause'} size="lg" onPointerDown={() => clip.ComponentView?.TogglePause?.()} />{' '} <FontAwesomeIcon icon="times" size="lg" onPointerDown={() => clip.ComponentView?.Pause?.()} />{' '} @@ -177,7 +177,7 @@ export class CollectionLinearView extends CollectionSubView() { addDocTab={this.props.addDocTab} pinToPres={emptyFunction} dragAction={(this.layoutDoc.childDragAction ?? this.props.childDragAction) as dropActionType} - rootSelected={this.props.isSelected} + rootSelected={this.rootSelected} removeDocument={this.props.removeDocument} ScreenToLocalTransform={docXf} PanelWidth={doc[Width]} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 05ec0448b..e7af91390 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -253,7 +253,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { return ( <DocumentView Document={childLayout} - DataDoc={childLayout.resolvedDataDoc as Doc} + TemplateDataDocument={childLayout.resolvedDataDoc as Doc} styleProvider={this.props.styleProvider} docViewPath={this.props.docViewPath} LayoutTemplate={this.props.childLayoutTemplate} @@ -261,7 +261,6 @@ export class CollectionMulticolumnView extends CollectionSubView() { renderDepth={this.props.renderDepth + 1} PanelWidth={width} PanelHeight={height} - shouldNotScale={shouldNotScale} rootSelected={this.rootSelected} dragAction={(this.props.Document.childDragAction ?? this.props.childDragAction) as dropActionType} onClick={this.onChildClickHandler} @@ -270,8 +269,8 @@ export class CollectionMulticolumnView extends CollectionSubView() { ScreenToLocalTransform={dxf} isContentActive={this.isChildContentActive} isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive} - hideResizeHandles={childLayout.layout_fitWidth || this.props.childHideResizeHandles?.() ? true : false} - hideDecorationTitle={this.props.childHideDecorationTitle?.()} + hideResizeHandles={childLayout.layout_fitWidth || this.props.childHideResizeHandles ? true : false} + hideDecorationTitle={this.props.childHideDecorationTitle} fitContentsToBox={this.props.fitContentsToBox} focus={this.props.focus} childFilters={this.childDocFilters} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index b4b62e635..ef66e8ce5 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -249,7 +249,7 @@ export class CollectionMultirowView extends CollectionSubView() { return ( <DocumentView Document={layout} - DataDoc={layout.resolvedDataDoc as Doc} + TemplateDataDocument={layout.resolvedDataDoc as Doc} styleProvider={this.props.styleProvider} docViewPath={this.props.docViewPath} LayoutTemplate={this.props.childLayoutTemplate} @@ -257,7 +257,6 @@ export class CollectionMultirowView extends CollectionSubView() { renderDepth={this.props.renderDepth + 1} PanelWidth={width} PanelHeight={height} - shouldNotScale={shouldNotScale} rootSelected={this.rootSelected} dropAction={StrCast(this.Document.childDragAction) as dropActionType} onClick={this.onChildClickHandler} @@ -265,8 +264,8 @@ export class CollectionMultirowView extends CollectionSubView() { ScreenToLocalTransform={dxf} isContentActive={this.isChildContentActive} isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive} - hideResizeHandles={layout.layout_fitWidth || this.props.childHideResizeHandles?.() ? true : false} - hideDecorationTitle={this.props.childHideDecorationTitle?.()} + hideResizeHandles={layout.layout_fitWidth || this.props.childHideResizeHandles ? true : false} + hideDecorationTitle={this.props.childHideDecorationTitle} fitContentsToBox={this.props.fitContentsToBox} dragAction={this.props.childDragAction} focus={this.props.focus} diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 22b80e21d..030f9953c 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -416,8 +416,8 @@ export class CollectionSchemaView extends CollectionSubView() { @action clearSelection = () => SelectionManager.DeselectAll(); - selectRows = (rootDoc: Doc, lastSelected: Doc) => { - const index = this.rowIndex(rootDoc); + selectRows = (doc: Doc, lastSelected: Doc) => { + const index = this.rowIndex(doc); const lastSelectedRow = this.rowIndex(lastSelected); const startRow = Math.min(lastSelectedRow, index); const endRow = Math.max(lastSelectedRow, index); @@ -892,7 +892,6 @@ export class CollectionSchemaView extends CollectionSubView() { {Array.from(this._selectedDocs).lastElement() && ( <DocumentView Document={Array.from(this._selectedDocs).lastElement()} - DataDoc={undefined} fitContentsToBox={returnTrue} dontCenter={'y'} onClickScriptDisable="always" @@ -971,7 +970,6 @@ class CollectionSchemaViewDoc extends React.Component<CollectionSchemaViewDocPro LayoutTemplate={this.props.schema.props.childLayoutTemplate} LayoutTemplateString={SchemaRowBox.LayoutString(this.props.schema.props.fieldKey, this.props.index)} Document={this.props.doc} - DataDoc={undefined} renderDepth={this.props.schema.props.renderDepth + 1} PanelWidth={this.tableWidthFunc} PanelHeight={this.props.rowHeight} diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 2c251d3cf..bd6d3bad6 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -72,7 +72,6 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> { searchFilterDocs: returnEmptyDoclist, styleProvider: DefaultStyleProvider, docViewPath: returnEmptyDoclist, - rootSelected: returnFalse, isSelected: returnFalse, setHeight: returnFalse, select: emptyFunction, @@ -273,7 +272,7 @@ export class SchemaRTFCell extends React.Component<SchemaTableCellProps> { fieldProps.isContentActive = this.selectedFunc; return ( <div className="schemaRTFCell" style={{ display: 'flex', fontStyle: this.selected ? undefined : 'italic', width: '100%', height: '100%', position: 'relative', color, textDecoration, cursor, pointerEvents }}> - {this.selected ? <FormattedTextBox {...fieldProps} DataDoc={this.props.Document} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))} + {this.selected ? <FormattedTextBox {...fieldProps} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))} </div> ); } diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index f2ab4ff4b..0ad76db35 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -41,22 +41,22 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b } else if (selectedViews.length) { if (checkResult) { const selView = selectedViews.lastElement(); - const fieldKey = selView.rootDoc.type === DocumentType.INK ? 'fillColor' : 'backgroundColor'; - const layoutFrameNumber = Cast(selView.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values - const contentFrameNumber = Cast(selView.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed - return CollectionFreeFormDocumentView.getStringValues(selView?.rootDoc, contentFrameNumber)[fieldKey] ?? 'transparent'; + const fieldKey = selView.Document.type === DocumentType.INK ? 'fillColor' : 'backgroundColor'; + const layoutFrameNumber = Cast(selView.props.docViewPath().lastElement()?.Document?._currentFrame, 'number'); // frame number that container is at which determines layout frame values + const contentFrameNumber = Cast(selView.Document?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed + return CollectionFreeFormDocumentView.getStringValues(selView?.Document, contentFrameNumber)[fieldKey] ?? 'transparent'; } selectedViews.some(dv => dv.ComponentView instanceof InkingStroke) && SetActiveFillColor(color ?? 'transparent'); selectedViews.forEach(dv => { - const fieldKey = dv.rootDoc.type === DocumentType.INK ? 'fillColor' : 'backgroundColor'; - const layoutFrameNumber = Cast(dv.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values - const contentFrameNumber = Cast(dv.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed + const fieldKey = dv.Document.type === DocumentType.INK ? 'fillColor' : 'backgroundColor'; + const layoutFrameNumber = Cast(dv.props.docViewPath().lastElement()?.Document?._currentFrame, 'number'); // frame number that container is at which determines layout frame values + const contentFrameNumber = Cast(dv.Document?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed if (contentFrameNumber !== undefined) { const obj: { [key: string]: Opt<string> } = {}; obj[fieldKey] = color; - CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, obj); + CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.Document, obj); } else { - dv.rootDoc['_' + fieldKey] = color; + dv.Document['_' + fieldKey] = color; } }); } else { @@ -377,12 +377,12 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | ' **/ ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) { const selected = SelectionManager.Views().lastElement(); - if (selected?.rootDoc.type === DocumentType.WEB) { + if (selected?.Document.type === DocumentType.WEB) { if (checkResult) { - return StrCast(selected.rootDoc.data, Cast(selected.rootDoc.data, WebField, null)?.url?.href); + return StrCast(selected.Document.data, Cast(selected.Document.data, WebField, null)?.url?.href); } selected.ComponentView?.setData?.(url); - //selected.rootDoc.data = new WebField(url); + //selected.Document.data = new WebField(url); } }); ScriptingGlobals.add(function webForward(checkResult?: boolean) { diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index 9dc133e28..3082c632d 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -54,7 +54,7 @@ export class LinkMenu extends React.Component<Props> { }; render() { - const sourceDoc = this.props.docView.rootDoc; + const sourceDoc = this.props.docView.Document; const sourceAnchor = this.props.docView.anchorViewDoc ?? sourceDoc; const style = this.props.style ?? (dv => ({ left: dv?.left || 0, top: this.props.docView.topMost ? undefined : (dv?.bottom || 0) + 15, bottom: this.props.docView.topMost ? 20 : undefined, maxWidth: 200 }))(this.props.docView.getBounds()); diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx index c1a5a634c..5453ed07b 100644 --- a/src/client/views/linking/LinkMenuGroup.tsx +++ b/src/client/views/linking/LinkMenuGroup.tsx @@ -46,14 +46,14 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> { const groupItems = Array.from(set.keys()).map(linkDoc => { const sourceDoc = this.props.docView.anchorViewDoc ?? - (this.props.docView.rootDoc.type === DocumentType.LINK // + (this.props.docView.Document.type === DocumentType.LINK // ? this.props.docView.props.LayoutTemplateString?.includes('link_anchor_1') ? DocCast(linkDoc.link_anchor_1) : DocCast(linkDoc.link_anchor_2) : this.props.sourceDoc); const destDoc = !sourceDoc ? undefined - : this.props.docView.rootDoc.type === DocumentType.LINK + : this.props.docView.Document.type === DocumentType.LINK ? this.props.docView.props.LayoutTemplateString?.includes('link_anchor_1') ? DocCast(linkDoc.link_anchor_2) : DocCast(linkDoc.link_anchor_1) diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index bf2a4e1a9..611771fff 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -82,7 +82,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> { }, emptyFunction, action(() => { - const trail = DocCast(this.props.docView.rootDoc.presentationTrail); + const trail = DocCast(this.props.docView.Document.presentationTrail); if (trail) { Doc.ActivePresentation = trail; DocumentViewInternal.addDocTabFunc(trail, OpenWhere.replaceRight); diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx index f02a284da..a36c8d778 100644 --- a/src/client/views/linking/LinkPopup.tsx +++ b/src/client/views/linking/LinkPopup.tsx @@ -61,7 +61,6 @@ export class LinkPopup extends React.Component<LinkPopupProps> { <SearchBox Document={Doc.MySearcher} - DataDoc={Doc.MySearcher} linkFrom={linkDoc} linkCreateAnchor={this.props.linkCreateAnchor} linkSearch={true} diff --git a/src/client/views/newlightbox/NewLightboxView.tsx b/src/client/views/newlightbox/NewLightboxView.tsx index bde93c761..f3cde3f5a 100644 --- a/src/client/views/newlightbox/NewLightboxView.tsx +++ b/src/client/views/newlightbox/NewLightboxView.tsx @@ -293,7 +293,6 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { <DocumentView ref={action((r: DocumentView | null) => (NewLightboxView._docView = r !== null ? r : undefined))} Document={LightboxView.LightboxDoc} - DataDoc={undefined} PanelWidth={this.newLightboxWidth} PanelHeight={this.newLightboxHeight} LayoutTemplate={NewLightboxView.LightboxDocTemplate} @@ -302,7 +301,6 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { styleProvider={DefaultStyleProvider} ScreenToLocalTransform={this.newLightboxScreenToLocal} renderDepth={0} - rootSelected={returnFalse} docViewPath={returnEmptyDoclist} childFilters={this.docFilters} childFiltersByRanges={returnEmptyFilter} diff --git a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx index 96846673b..a085e4a66 100644 --- a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx +++ b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx @@ -19,7 +19,7 @@ export const Recommendation = (props: IRecommendation) => { if (source == 'Dash' && docId) { const docView = DocumentManager.Instance.getDocumentViewsById(docId).lastElement(); if (docView) { - doc = docView.rootDoc; + doc = docView.Document; } } else if (data) { switch (type) { diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 807a77233..dacdbe986 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -139,17 +139,17 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp const timecode = Cast(this.layoutDoc._layout_currentTimecode, 'number', null); const anchor = addAsAnnotation ? CollectionStackedTimeline.createAnchor( - this.rootDoc, + this.Document, this.dataDoc, this.annotationKey, this._ele?.currentTime || Cast(this.props.Document._layout_currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined), undefined, undefined, addAsAnnotation - ) || this.rootDoc - : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.rootDoc }); + ) || this.Document + : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.Document }); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.rootDoc); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.Document); return anchor; }; @@ -363,14 +363,14 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp returnFalse, returnFalse, action(() => { - const newDoc = DocUtils.GetNewTextDoc('', NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height)); + const newDoc = DocUtils.GetNewTextDoc('', NumCast(this.Document.x), NumCast(this.Document.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height)); const textField = Doc.LayoutFieldKey(newDoc); Doc.GetProto(newDoc)[`${textField}_recordingSource`] = this.dataDoc; Doc.GetProto(newDoc)[`${textField}_recordingStart`] = ComputedField.MakeFunction(`this.${textField}_recordingSource.${this.fieldKey}_recordingStart`); Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction(`this.${textField}_recordingSource.mediaState`); - if (Doc.IsInMyOverlay(this.rootDoc)) { - newDoc.overlayX = this.rootDoc.x; - newDoc.overlayY = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height); + if (Doc.IsInMyOverlay(this.Document)) { + newDoc.overlayX = this.Document.x; + newDoc.overlayY = NumCast(this.Document.y) + NumCast(this.layoutDoc._height); Doc.AddToMyOverlay(newDoc); } else { this.props.addDocument?.(newDoc); @@ -424,7 +424,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp // plays link playLink = (link: Doc, options: DocFocusOptions) => { - if (link.annotationOn === this.rootDoc) { + if (link.annotationOn === this.Document) { if (!this.layoutDoc.dontAutoPlayFollowedLinks) { this.playFrom(this.timeline?.anchorStart(link) || 0, this.timeline?.anchorEnd(link)); } else { diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 5b4fb3e29..6195a155d 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -36,7 +36,7 @@ export interface CollectionFreeFormDocumentViewWrapperProps extends DocumentView CollectionFreeFormView: CollectionFreeFormView; } @observer -export class CollectionFreeFormDocumentViewWrapper extends DocComponent<CollectionFreeFormDocumentViewWrapperProps>() implements CollectionFreeFormDocumentViewProps { +export class CollectionFreeFormDocumentViewWrapper extends DocComponent<CollectionFreeFormDocumentViewWrapperProps & { fieldKey: string }>() implements CollectionFreeFormDocumentViewProps { @observable X = this.props.x; @observable Y = this.props.y; @observable Z = this.props.z; @@ -48,6 +48,7 @@ export class CollectionFreeFormDocumentViewWrapper extends DocComponent<Collecti @observable Highlight = this.props.highlight; @observable Width = this.props.width; @observable Height = this.props.height; + @observable AutoDim = this.props.autoDim; @observable Transition = this.props.transition; @observable DataTransition = this.props.dataTransition; CollectionFreeFormView = this.props.CollectionFreeFormView; // needed for type checking @@ -71,6 +72,7 @@ export class CollectionFreeFormDocumentViewWrapper extends DocComponent<Collecti w_Highlight = () => this.Highlight; // prettier-ignore w_Width = () => this.Width; // prettier-ignore w_Height = () => this.Height; // prettier-ignore + w_AutoDim = () => this.AutoDim; w_Transition = () => this.Transition; // prettier-ignore w_DataTransition = () => this.DataTransition; // prettier-ignore @@ -113,7 +115,7 @@ export interface CollectionFreeFormDocumentViewProps { } @observer -export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps & DocumentViewProps>() { +export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps & DocumentViewProps & { fieldKey: string }>() { get displayName() { // this makes mobx trace() statements more descriptive return 'CollectionFreeFormDocumentView(' + this.Document.title + ')'; } // prettier-ignore @@ -252,7 +254,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF // undefined - this is not activated by a group isGroupActive = () => { if (this.CollectionFreeFormView.isAnyChildContentActive()) return undefined; - const isGroup = this.Document.isGroup && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent'); + const isGroup = this.dataDoc.isGroup && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent'); return isGroup ? (this.props.isDocumentActive?.() ? 'group' : this.props.isGroupActive?.() ? 'child' : 'inactive') : this.props.isGroupActive?.() ? 'child' : undefined; }; public static CollectionFreeFormDocViewClassName = 'collectionFreeFormDocumentView-container'; diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx index 4c7ea4002..2a20d935b 100644 --- a/src/client/views/nodes/ColorBox.tsx +++ b/src/client/views/nodes/ColorBox.tsx @@ -60,7 +60,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() { style={{ transform: `scale(${scaling})`, width: `${100 * scaling}%`, height: `${100 * scaling}%` }}> <SketchPicker onChange={c => Doc.ActiveTool === InkTool.None && ColorBox.switchColor(c)} - color={StrCast(SelectionManager.Views()?.[0]?.rootDoc?._backgroundColor, ActiveInkColor())} + color={StrCast(SelectionManager.Views()?.[0]?.Document?._backgroundColor, ActiveInkColor())} presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']} /> @@ -74,8 +74,8 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() { onChange={(e: React.ChangeEvent<HTMLInputElement>) => { SetActiveInkWidth(e.target.value); SelectionManager.Views() - .filter(i => StrCast(i.rootDoc.type) === DocumentType.INK) - .map(i => (i.rootDoc.stroke_width = Number(e.target.value))); + .filter(i => StrCast(i.Document.type) === DocumentType.INK) + .map(i => (i.Document.stroke_width = Number(e.target.value))); }} /> </div> diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index ff394e5f5..9fd4fbcaa 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -44,7 +44,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl private internalDrop = (e: Event, dropEvent: DragManager.DropEvent, fieldKey: string) => { if (dropEvent.complete.docDragData) { const droppedDocs = dropEvent.complete.docDragData?.droppedDocuments; - const added = dropEvent.complete.docDragData.moveDocument?.(droppedDocs, this.rootDoc, (doc: Doc | Doc[]) => this.addDoc(doc instanceof Doc ? doc : doc.lastElement(), fieldKey)); + const added = dropEvent.complete.docDragData.moveDocument?.(droppedDocs, this.Document, (doc: Doc | Doc[]) => this.addDoc(doc instanceof Doc ? doc : doc.lastElement(), fieldKey)); Doc.SetContainer(droppedDocs.lastElement(), this.dataDoc); !added && e.preventDefault(); e.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place @@ -93,18 +93,18 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { const anchor = Docs.Create.ConfigDocument({ - title: 'CompareAnchor:' + this.rootDoc.title, + title: 'CompareAnchor:' + this.Document.title, // set presentation timing properties for restoring view presentation_transition: 1000, - annotationOn: this.rootDoc, + annotationOn: this.Document, }); if (anchor) { if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; /* addAsAnnotation &&*/ this.addDocument(anchor); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), clippable: true } }, this.rootDoc); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), clippable: true } }, this.Document); return anchor; } - return this.rootDoc; + return this.Document; }; @undoBatch @@ -173,7 +173,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl <DocumentView {...this.props} Document={targetDoc} - DataDoc={undefined} + TemplateDataDocument={undefined} moveDocument={which.endsWith('1') ? this.moveDoc1 : this.moveDoc2} removeDocument={which.endsWith('1') ? this.remDoc1 : this.remDoc2} NativeWidth={returnZero} diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 1cb0a50f3..a626772e4 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -35,7 +35,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { // all CSV records in the dataset (that aren't an empty row) @computed.struct get records() { - var records = DataVizBox.dataset.get(CsvCast(this.rootDoc[this.fieldKey]).url.href); + var records = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href); return records?.filter(record => Object.keys(record).some(key => record[key])) ?? []; } @@ -75,7 +75,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { }; getAnchor = (addAsAnnotation?: boolean, pinProps?: PinProps) => { const anchor = !pinProps - ? this.rootDoc + ? this.Document : this._vizRenderer?.getAnchor(pinProps) ?? Docs.Create.ConfigDocument({ // when we clear selection -> we should have it so chartBox getAnchor returns undefined @@ -99,20 +99,20 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { componentDidMount() { this.props.setContentView?.(this); - if (!DataVizBox.dataset.has(CsvCast(this.rootDoc[this.fieldKey]).url.href)) this.fetchData(); + if (!DataVizBox.dataset.has(CsvCast(this.dataDoc[this.fieldKey]).url.href)) this.fetchData(); } fetchData = () => { - DataVizBox.dataset.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, []); // assign temporary dataset as a lock to prevent duplicate server requests + DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey]).url.href, []); // assign temporary dataset as a lock to prevent duplicate server requests fetch('/csvData?uri=' + this.dataUrl?.url.href) // - .then(res => res.json().then(action(res => !res.errno && DataVizBox.dataset.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, res)))); + .then(res => res.json().then(action(res => !res.errno && DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey]).url.href, res)))); }; // toggles for user to decide which chart type to view the data in @computed get renderVizView() { const scale = this.props.NativeDimScaling?.() || 1; const sharedProps = { - rootDoc: this.rootDoc, + Document: this.Document, layoutDoc: this.layoutDoc, records: this.records, axes: this.axes, diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index e67e2bf31..a1bd316f0 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -10,14 +10,13 @@ import { List } from '../../../../../fields/List'; import { listSpec } from '../../../../../fields/Schema'; import { Cast, DocCast, StrCast } from '../../../../../fields/Types'; import { Docs } from '../../../../documents/Documents'; -import { LinkManager } from '../../../../util/LinkManager'; import { undoable } from '../../../../util/UndoManager'; import { PinProps, PresBox } from '../../trails'; import { scaleCreatorNumerical, yAxisCreator } from '../utils/D3Utils'; import './Chart.scss'; export interface HistogramProps { - rootDoc: Doc; + Document: Doc; layoutDoc: Doc; axes: string[]; records: { [key: string]: any }[]; @@ -83,9 +82,9 @@ export class Histogram extends React.Component<HistogramProps> { } @computed get parentViz() { - return DocCast(this.props.rootDoc.dataViz_parentViz); - // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links - // .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link + return DocCast(this.props.Document.dataViz_parentViz); + // return LinkManager.Instance.getAllRelatedLinks(this.props.Document) // out of all links + // .filter(link => link.link_anchor_1 == this.props.Document.dataViz_parentViz) // get links where this chart doc is the target of the link // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link } @@ -115,7 +114,7 @@ export class Histogram extends React.Component<HistogramProps> { const anchor = Docs.Create.ConfigDocument({ title: 'histogram doc selection' + this._currSelected, }); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.Document); return anchor; }; diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index 3de7a0c4a..7e0391879 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -23,7 +23,7 @@ export interface SelectedDataPoint extends DataPoint { elem?: d3.Selection<d3.BaseType, unknown, SVGGElement, unknown>; } export interface LineChartProps { - rootDoc: Doc; + Document: Doc; layoutDoc: Doc; axes: string[]; records: { [key: string]: any }[]; @@ -63,10 +63,10 @@ export class LineChart extends React.Component<LineChartProps> { return this.props.axes[1] + ' vs. ' + this.props.axes[0] + ' Line Chart'; } @computed get parentViz() { - return DocCast(this.props.rootDoc.dataViz_parentViz); - // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links + return DocCast(this.props.Document.dataViz_parentViz); + // return LinkManager.Instance.getAllRelatedLinks(this.props.Document) // out of all links // .filter(link => { - // return link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz; + // return link.link_anchor_1 == this.props.Document.dataViz_parentViz; // }) // get links where this chart doc is the target of the link // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link } @@ -74,7 +74,7 @@ export class LineChart extends React.Component<LineChartProps> { // return selected x and y axes // otherwise, use the selection of whatever is linked to us const incomingVizBox = DocumentManager.Instance.getFirstDocumentView(this.parentViz)?.ComponentView as DataVizBox; - const highlitedRowIds = NumListCast(incomingVizBox?.rootDoc?.dataViz_highlitedRows); + const highlitedRowIds = NumListCast(incomingVizBox?.layoutDoc?.dataViz_highlitedRows); return this._tableData.filter((record, i) => highlitedRowIds.includes(this._tableDataIds[i])); // get all the datapoints they have selected field set by incoming anchor } @computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } { @@ -170,7 +170,7 @@ export class LineChart extends React.Component<LineChartProps> { // title: 'line doc selection' + this._currSelected?.x, }); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.Document); anchor.config_dataVizSelection = this._currSelected ? new List<number>([this._currSelected.x, this._currSelected.y]) : undefined; return anchor; }; diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index 7303eb184..4dbd67485 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -15,7 +15,7 @@ import './Chart.scss'; import { Checkbox } from '@material-ui/core'; export interface PieChartProps { - rootDoc: Doc; + Document: Doc; layoutDoc: Doc; axes: string[]; records: { [key: string]: any }[]; @@ -76,9 +76,9 @@ export class PieChart extends React.Component<PieChartProps> { } @computed get parentViz() { - return DocCast(this.props.rootDoc.dataViz_parentViz); - // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links - // .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link + return DocCast(this.props.Document.dataViz_parentViz); + // return LinkManager.Instance.getAllRelatedLinks(this.props.Document) // out of all links + // .filter(link => link.link_anchor_1 == this.props.Document.dataViz_parentViz) // get links where this chart doc is the target of the link // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link } @@ -101,7 +101,7 @@ export class PieChart extends React.Component<PieChartProps> { // title: 'piechart doc selection' + this._currSelected, }); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.Document); return anchor; }; diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 147dfb182..9ac06cf3c 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -14,7 +14,7 @@ import { DATA_VIZ_TABLE_ROW_HEIGHT } from '../../../global/globalCssVariables.sc import './Chart.scss'; interface TableBoxProps { - rootDoc: Doc; + Document: Doc; layoutDoc: Doc; records: { [key: string]: any }[]; selectAxes: (axes: string[]) => void; @@ -57,9 +57,9 @@ export class TableBox extends React.Component<TableBoxProps> { } @computed get parentViz() { - return DocCast(this.props.rootDoc.dataViz_parentViz); - // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links - // .filter(link => link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz) // get links where this chart doc is the target of the link + return DocCast(this.props.Document.dataViz_parentViz); + // return LinkManager.Instance.getAllRelatedLinks(this.props.Document) // out of all links + // .filter(link => link.link_anchor_1 == this.props.Document.dataViz_parentViz) // get links where this chart doc is the target of the link // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link } @@ -119,12 +119,12 @@ export class TableBox extends React.Component<TableBoxProps> { e, e => { // dragging off a column to create a brushed DataVizBox - const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!; + const sourceAnchorCreator = () => this.props.docView?.()!.Document!; const targetCreator = (annotationOn: Doc | undefined) => { - const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!); + const embedding = Doc.MakeEmbedding(this.props.docView?.()!.Document!); embedding._dataViz = DataVizView.TABLE; embedding._dataViz_axes = new List<string>([col, col]); - embedding._dataViz_parentViz = this.props.rootDoc; + embedding._dataViz_parentViz = this.props.Document; embedding.annotationOn = annotationOn; embedding.histogramBarColors = Field.Copy(this.props.layoutDoc.histogramBarColors); embedding.defaultHistogramColor = this.props.layoutDoc.defaultHistogramColor; @@ -138,7 +138,7 @@ export class TableBox extends React.Component<TableBoxProps> { e.linkDocument.link_displayLine = true; e.linkDocument.link_matchEmbeddings = true; e.linkDocument.link_displayArrow = true; - // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc; + // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.Document; // e.annoDragData.linkSourceDoc.followLinkZoom = false; } }, diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index eed787b6d..4b58bfe48 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -1,7 +1,8 @@ -import { computed, trace } from 'mobx'; +import { computed } from 'mobx'; import { observer } from 'mobx-react'; +import { MouseEventHandler } from 'react-select'; import { Doc, Opt } from '../../../fields/Doc'; -import { AclPrivate } from '../../../fields/DocSymbols'; +import { AclPrivate, DocData } from '../../../fields/DocSymbols'; import { ScriptField } from '../../../fields/ScriptField'; import { Cast, StrCast } from '../../../fields/Types'; import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; @@ -36,6 +37,7 @@ import { LinkAnchorBox } from './LinkAnchorBox'; import { LinkBox } from './LinkBox'; import { LoadingBox } from './LoadingBox'; import { MapBox } from './MapBox/MapBox'; +import { MapPushpinBox } from './MapBox/MapPushpinBox'; import { PDFBox } from './PDFBox'; import { PhysicsSimulationBox } from './PhysicsBox/PhysicsSimulationBox'; import { RecordingBox } from './RecordingBox'; @@ -46,7 +48,6 @@ import { VideoBox } from './VideoBox'; import { WebBox } from './WebBox'; import React = require('react'); import XRegExp = require('xregexp'); -import { MapPushpinBox } from './MapBox/MapPushpinBox'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? @@ -117,10 +118,11 @@ export class HTMLtag extends React.Component<HTMLtagProps> { @observer export class DocumentContentsView extends React.Component< - DocumentViewProps & { - setHeight?: (height: number) => void; - layout_fieldKey: string; - } + DocumentViewProps & + FieldViewProps & { + setHeight?: (height: number) => void; + layout_fieldKey: string; + } > { @computed get layout(): string { TraceMobx(); @@ -137,10 +139,6 @@ export class DocumentContentsView extends React.Component< Object.keys(prevProps).forEach(pkey => (prevProps as any)[pkey] !== (this.props as any)[pkey] && console.log(pkey + ' ' + (prevProps as any)[pkey] + ' ' + (this.props as any)[pkey])); } - get dataDoc() { - const proto = this.props.DataDoc || Doc.GetProto(this.props.Document); - return proto instanceof Promise ? undefined : proto; - } get layoutDoc() { // bcz: replaced this with below : is it correct? change was made to accommodate passing fieldKey's from a layout script // const template: Doc = this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document, this.props.layout_fieldKey ? Cast(this.props.Document[this.props.layout_fieldKey], Doc, null) : undefined); @@ -163,19 +161,24 @@ export class DocumentContentsView extends React.Component< 'LayoutTemplate', 'dontCenter', 'contextMenuItems', - 'onClick', + //'onClick', // don't need to omit this since it will be set 'onDoubleClick', 'onPointerDown', 'onPointerUp', ]; - const list = { - ...OmitKeys(this.props, [...docOnlyProps], '').omit, - Document: this.layoutDoc, - DataDoc: this.dataDoc, - onClick: onClick, - onInput: onInput, + const templateDataDoc = this.props.TemplateDataDocument ?? (this.layoutDoc !== this.props.Document ? this.props.Document[DocData] : undefined); + const list: BindingProps & React.DetailedHTMLProps<React.HtmlHTMLAttributes<HTMLDivElement>, HTMLDivElement> = { + ...this.props, + Document: this.layoutDoc ?? this.props.Document, + TemplateDataDocument: templateDataDoc instanceof Promise ? undefined : templateDataDoc, + onClick: onClick as any as MouseEventHandler, // pass onClick script as if it were a real function -- it will be interpreted properly in the HTMLtag + onInput: onInput as any as React.FormEventHandler, + }; + return { + props: { + ...OmitKeys(list, [...docOnlyProps], '').omit, + }, }; - return { props: list }; } // componentWillUpdate(oldProps: any, newState: any) { diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx index bccbd66e8..49592d993 100644 --- a/src/client/views/nodes/DocumentIcon.tsx +++ b/src/client/views/nodes/DocumentIcon.tsx @@ -33,7 +33,7 @@ export class DocumentIcon extends React.Component<{ view: DocumentView; index: n background: SettingsManager.userBackgroundColor, transform: `translate(${(left + right) / 2}px, ${top}px)`, }}> - <Tooltip title={<>{this.props.view.rootDoc.title}</>}> + <Tooltip title={<>{this.props.view.Document.title}</>}> <p>d{this.props.index}</p> </Tooltip> </div> @@ -59,7 +59,7 @@ export class DocumentIconContainer extends React.Component { const match = node.text.match(/d([0-9]+)/); if (match) { const m = parseInt(match[1]); - const doc = DocumentIcon.DocViews[m].rootDoc; + const doc = DocumentIcon.DocViews[m].Document; usedDocuments.add(m); return factory.createIdentifier(`idToDoc("${doc[Id]}")`); } diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 50a7f5d7b..ce3650749 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -69,7 +69,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => { - doubleTap && DocumentView.showBackLinks(this.props.View.rootDoc); + doubleTap && DocumentView.showBackLinks(this.props.View.Document); }), undefined, undefined, diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f665c69ab..40ae1459f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -5,7 +5,7 @@ import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal'; import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc'; -import { AclPrivate, Animation, AudioPlay, DocData, DocViews } from '../../../fields/DocSymbols'; +import { AclPrivate, Animation, AudioPlay, DocViews } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; @@ -109,8 +109,10 @@ export interface DocFocusOptions { easeFunc?: 'linear' | 'ease'; // transition method for scrolling } export type DocFocusFunc = (doc: Doc, options: DocFocusOptions) => Opt<number>; -export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any; +export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps | FieldViewProps>, property: string) => any; export interface DocComponentView { + fieldKey?: string; + annotationKey?: string; updateIcon?: () => void; // updates the icon representation of the document getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box) restoreView?: (viewSpec: Doc) => boolean; @@ -119,10 +121,8 @@ export interface DocComponentView { getView?: (doc: Doc) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections) - ignoreNativeDimScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling. select?: (ctrlKey: boolean, shiftKey: boolean) => void; focus?: (textAnchor: Doc, options: DocFocusOptions) => Opt<number>; - menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected. isAnyChildContentActive?: () => boolean; // is any child content of the document active onClickScriptDisable?: () => 'never' | 'always'; // disable click scripts : never, always, or undefined = only when selected getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) @@ -136,12 +136,7 @@ export interface DocComponentView { componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null; dragStarting?: (snapToDraggedDoc: boolean, showGroupDragTarget: boolean, visited: Set<Doc>) => void; incrementalRendering?: () => void; - layout_fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox) - overridePointerEvents?: () => 'all' | 'none' | undefined; // if the conmponent overrides the pointer events for the document (e.g, KeyValueBox always allows pointer events) - fieldKey?: string; - annotationKey?: string; infoUI?: () => JSX.Element; - getTitle?: () => string; getCenter?: (xf: Transform) => { X: number; Y: number }; screenBounds?: () => Opt<{ left: number; top: number; right: number; bottom: number; center?: { X: number; Y: number } }>; ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number }; @@ -151,33 +146,28 @@ export interface DocComponentView { } // These props are passed to both FieldViews and DocumentViews export interface DocumentViewSharedProps { - fieldKey?: string; // only used by FieldViews but helpful here to allow styleProviders to access fieldKey of FieldViewProps. In priniciple, passing a fieldKey to a documentView could override or be the default fieldKey for fieldViews - DocumentView?: () => DocumentView; renderDepth: number; Document: Doc; - DataDoc?: Doc; + TemplateDataDocument?: Doc; + scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document + DocumentView?: () => DocumentView; + CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView; fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document isGroupActive?: () => string | undefined; // is this document part of a group that is active - suppressSetHeight?: boolean; setContentView?: (view: DocComponentView) => any; - CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView; PanelWidth: () => number; PanelHeight: () => number; - shouldNotScale?: () => boolean; docViewPath: () => DocumentView[]; - childHideDecorationTitle?: () => boolean; - childHideResizeHandles?: () => boolean; - childDragAction?: dropActionType; // allows child documents to be dragged out of collection without holding the embedKey or dragging the doc decorations title bar. + childFilters: () => string[]; + childFiltersByRanges: () => string[]; styleProvider: Opt<StyleProviderFunc>; setTitleFocus?: () => void; focus: DocFocusFunc; layout_fitWidth?: (doc: Doc) => boolean | undefined; - childFilters: () => string[]; - childFiltersByRanges: () => string[]; searchFilterDocs: () => Doc[]; layout_showTitle?: () => string; whenChildContentsActiveChanged: (isActive: boolean) => void; - rootSelected: () => boolean; // whether the root of a template has been selected + rootSelected?: () => boolean; // whether the root of a template has been selected addDocTab: (doc: Doc, where: OpenWhere) => boolean; filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example) addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; @@ -186,26 +176,27 @@ export interface DocumentViewSharedProps { pinToPres: (document: Doc, pinProps: PinProps) => void; ScreenToLocalTransform: () => Transform; bringToFront: (doc: Doc, sendToBack?: boolean) => void; - dragAction?: dropActionType; + waitForDoubleClickToClick?: () => 'never' | 'always' | undefined; + defaultDoubleClick?: () => 'default' | 'ignore' | undefined; + pointerEvents?: () => Opt<string>; treeViewDoc?: Doc; xPadding?: number; yPadding?: number; - dropAction?: dropActionType; dontRegisterView?: boolean; + childHideDecorationTitle?: boolean; + childHideResizeHandles?: boolean; + childDragAction?: dropActionType; // allows child documents to be dragged out of collection without holding the embedKey or dragging the doc decorations title bar. + dropAction?: dropActionType; + dragAction?: dropActionType; dragWhenActive?: boolean; + dontHideOnDrag?: boolean; hideLinkButton?: boolean; hideCaptions?: boolean; ignoreAutoHeight?: boolean; forceAutoHeight?: boolean; + suppressSetHeight?: boolean; disableBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over. onClickScriptDisable?: 'never' | 'always'; // undefined = only when selected - waitForDoubleClickToClick?: () => 'never' | 'always' | undefined; - defaultDoubleClick?: () => 'default' | 'ignore' | undefined; - pointerEvents?: () => Opt<string>; - scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document - createNewFilterDoc?: () => void; - updateFilterDoc?: (doc: Doc) => void; - dontHideOnDrag?: boolean; } // these props are specific to DocuentViews @@ -219,12 +210,11 @@ export interface DocumentViewProps extends DocumentViewSharedProps { hideOpenButton?: boolean; hideDeleteButton?: boolean; hideLinkAnchors?: boolean; - isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events - isContentActive: () => boolean | undefined; // whether document contents should handle pointer events contentPointerEvents?: 'none' | 'all' | undefined; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents - radialMenu?: String[]; LayoutTemplateString?: string; dontCenter?: 'x' | 'y' | 'xy'; + isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events + isContentActive: () => boolean | undefined; // whether document contents should handle pointer events NativeWidth?: () => number; NativeHeight?: () => number; NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to FieldViewProps @@ -242,8 +232,6 @@ export interface DocumentViewProps extends DocumentViewSharedProps { // these props are only available in DocumentViewIntenral export interface DocumentViewInternalProps extends DocumentViewProps { - NativeWidth: () => number; - NativeHeight: () => number; isSelected: () => boolean; select: (ctrlPressed: boolean, shiftPress?: boolean) => void; DocumentView: () => DocumentView; @@ -327,12 +315,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps @computed get finalLayoutKey() { return StrCast(this.Document.layout_fieldKey, 'layout'); } - @computed get nativeWidth() { - return this.props.NativeWidth(); - } - @computed get nativeHeight() { - return this.props.NativeHeight(); - } @computed get disableClickScriptFunc() { const onScriptDisable = this.props.onClickScriptDisable ?? this._componentView?.onClickScriptDisable?.() ?? this.layoutDoc.onClickScriptDisable; // prettier-ignore @@ -507,7 +489,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps clickFunc = () => UndoManager.RunInBatch(func, 'click ' + this.Document.title); } else { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part - if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) { + if ((this.layoutDoc.onDragStart || this.props.TemplateDataDocument) && !(e.ctrlKey || e.button > 0)) { stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template } preventDefault = false; @@ -549,7 +531,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps this._downX = e.clientX; this._downY = e.clientY; this._downTime = Date.now(); - if ((Doc.ActiveTool === InkTool.None || this.props.addDocTab === returnFalse) && !(this.props.Document.rootDocument && !(e.ctrlKey || e.button > 0))) { + if ((Doc.ActiveTool === InkTool.None || this.props.addDocTab === returnFalse) && !(this.props.TemplateDataDocument && !(e.ctrlKey || e.button > 0))) { // click events stop here if the document is active and no modes are overriding it // if this is part of a template, let the event go up to the template root unless right/ctrl clicking if ( @@ -762,7 +744,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps if (this.props.renderDepth === 0) { appearanceItems.splice(0, 0, { description: 'Open in Lightbox', event: () => LightboxView.Instance.SetLightboxDoc(this.Document), icon: 'external-link-alt' }); } - this.Document.type === DocumentType.PRES && appearanceItems.push({ description: 'Pin', event: () => this.props.pinToPres(this.Document, {}), icon: 'eye' }); + appearanceItems.push({ description: 'Pin', event: () => this.props.pinToPres(this.Document, {}), icon: 'eye' }); !Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template ', event: () => this.props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' }); !appearance && appearanceItems.length && cm.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'compass' }); @@ -815,7 +797,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps const moreItems = more && 'subitems' in more ? more.subitems : []; if (!Doc.IsSystem(this.Document)) { 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: 'Make View of Metadata Field', event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.TemplateDataDocument), icon: 'concierge-bell' }); moreItems.push({ description: `${this.Document._chromeHidden ? 'Show' : 'Hide'} Chrome`, event: () => (this.Document._chromeHidden = !this.Document._chromeHidden), icon: 'project-diagram' }); if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) { @@ -899,13 +881,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps }; @computed get _rootSelected() { - return this.props.isSelected() || BoolCast(this.Document.rootDocument && this.props.rootSelected?.()); + return this.props.isSelected() || BoolCast(this.props.TemplateDataDocument && this.props.rootSelected?.()); } rootSelected = () => this._rootSelected; panelHeight = () => this.props.PanelHeight() - this.headerMargin; screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin); onClickFunc: any = () => (this.disableClickScriptFunc ? undefined : this.onClickHandler); - setHeight = (height: number) => (this.layoutDoc._height = height); + setHeight = (height: number) => !this.props.suppressSetHeight && (this.layoutDoc._height = height); setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view)); @observable _isContentActive: boolean | undefined; @@ -939,17 +921,17 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps <DocumentContentsView key={1} {...this.props} + fieldKey="" pointerEvents={this.contentPointerEvents} docViewPath={this.props.viewPath} setContentView={this.setContentView} childFilters={this.childFilters} PanelHeight={this.panelHeight} - setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined} + setHeight={this.setHeight} isContentActive={this.isContentActive} ScreenToLocalTransform={this.screenToLocal} rootSelected={this.rootSelected} onClick={this.onClickFunc} - focus={this.props.focus} setTitleFocus={this.setTitleFocus} layout_fieldKey={this.finalLayoutKey} /> @@ -1386,11 +1368,11 @@ export class DocumentView extends React.Component<DocumentViewProps> { }; public setViewTransition = (transProp: string, timeInMs: number, afterTrans?: () => void, dataTrans = false) => { this.layoutDoc._viewTransition = `${transProp} ${timeInMs}ms`; - if (dataTrans) this.rootDoc._dataTransition = `${transProp} ${timeInMs}ms`; + if (dataTrans) this.Document._dataTransition = `${transProp} ${timeInMs}ms`; this.ViewTimer && clearTimeout(this.ViewTimer); return (this.ViewTimer = setTimeout(() => { this.layoutDoc._viewTransition = undefined; - this.rootDoc._dataTransition = 'inherit'; + this.Document._dataTransition = 'inherit'; afterTrans?.(); }, timeInMs + 10)); }; @@ -1428,9 +1410,6 @@ export class DocumentView extends React.Component<DocumentViewProps> { get topMost() { return this.props.renderDepth === 0; } - get rootDoc() { - return this.Document; - } get dataDoc() { return this.docView?.dataDoc ?? this.Document; } @@ -1441,21 +1420,21 @@ export class DocumentView extends React.Component<DocumentViewProps> { return this.docView?._componentView; } get allLinks() { - return (this.docView?.allLinks || []).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.Document || link.link_anchor_2 === this.rootDoc); + return (this.docView?.allLinks || []).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document); } get LayoutFieldKey() { return this.docView?.LayoutFieldKey || 'layout'; } @computed get layout_fitWidth() { - return this.docView?._componentView?.layout_fitWidth?.() ?? this.props.layout_fitWidth?.(this.layoutDoc) ?? this.layoutDoc?.layout_fitWidth; + return this.props.layout_fitWidth?.(this.layoutDoc) ?? this.layoutDoc?.layout_fitWidth; } @computed get anchorViewDoc() { return this.props.LayoutTemplateString?.includes('link_anchor_2') ? DocCast(this.Document['link_anchor_2']) : this.props.LayoutTemplateString?.includes('link_anchor_1') ? DocCast(this.Document['link_anchor_1']) : undefined; } @computed get hideLinkButton() { - return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkBtn + (this.isSelected() ? ':selected' : '')); + return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkBtn + (this.SELECTED ? ':selected' : '')); } - hideLinkCount = () => this.props.renderDepth === -1 || (this.isSelected() && this.props.renderDepth) || !this._isHovering || this.hideLinkButton; + hideLinkCount = () => this.props.renderDepth === -1 || (this.SELECTED && this.props.renderDepth) || !this._isHovering || this.hideLinkButton; @computed get linkCountView() { return <DocumentLinksButton hideCount={this.hideLinkCount} View={this} scaling={this.scaleToScreenSpace} OnHover={true} Bottom={this.topMost} ShowCount={true} />; } @@ -1466,13 +1445,13 @@ export class DocumentView extends React.Component<DocumentViewProps> { return Doc.Layout(this.Document, this.props.LayoutTemplate?.()); } @computed get nativeWidth() { - return this.docView?._componentView?.ignoreNativeDimScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth)); + return this.props.LayoutTemplateString?.includes(KeyValueBox.name) ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.TemplateDataDocument, !this.layout_fitWidth)); } @computed get nativeHeight() { - return this.docView?._componentView?.ignoreNativeDimScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth)); + return this.props.LayoutTemplateString?.includes(KeyValueBox.name) ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.TemplateDataDocument, !this.layout_fitWidth)); } @computed get shouldNotScale() { - return this.props.shouldNotScale?.() || (this.layout_fitWidth && !this.nativeWidth) || [CollectionViewType.Docking].includes(this.Document._type_collection as any); + return (this.layout_fitWidth && !this.nativeWidth) || this.props.LayoutTemplateString?.includes(KeyValueBox.name) || [CollectionViewType.Docking].includes(this.Document._type_collection as any); } @computed get effectiveNativeWidth() { return this.shouldNotScale ? 0 : this.nativeWidth || NumCast(this.layoutDoc.width); @@ -1593,7 +1572,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { docViewPathFunc = () => this.docViewPath; isSelected = () => this.SELECTED; select = (extendSelection: boolean, focusSelection?: boolean) => { - if (this.isSelected() && SelectionManager.Views().length > 1) SelectionManager.DeselectView(this); + if (this.SELECTED && SelectionManager.Views().length > 1) SelectionManager.DeselectView(this); else { SelectionManager.SelectView(this, extendSelection); if (focusSelection) { @@ -1692,7 +1671,6 @@ export class DocumentView extends React.Component<DocumentViewProps> { {...this.props} DocumentView={this.selfView} viewPath={this.docViewPathFunc} - shouldNotScale={this.ShouldNotScale} PanelWidth={this.PanelWidth} PanelHeight={this.PanelHeight} NativeWidth={this.NativeWidth} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index f7f94c546..219e3025a 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -16,7 +16,6 @@ import { DocumentViewSharedProps } from './DocumentView'; export interface FieldViewProps extends DocumentViewSharedProps { // FieldView specific props that are not part of DocumentView props fieldKey: string; - scrollOverflow?: boolean; // bcz: would like to think this can be avoided -- need to look at further select: (isCtrlPressed: boolean) => void; isContentActive: (outsideReaction?: boolean) => boolean | undefined; diff --git a/src/client/views/nodes/FontIconBox/ButtonInterface.ts b/src/client/views/nodes/FontIconBox/ButtonInterface.ts index 0aa2ac8e1..1c034bfbe 100644 --- a/src/client/views/nodes/FontIconBox/ButtonInterface.ts +++ b/src/client/views/nodes/FontIconBox/ButtonInterface.ts @@ -1,12 +1,12 @@ -import { Doc } from "../../../../fields/Doc"; -import { IconProp } from "@fortawesome/fontawesome-svg-core"; -import { ButtonType } from "./FontIconBox"; +import { Doc } from '../../../../fields/Doc'; +import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { ButtonType } from './FontIconBox'; export interface IButtonProps { type: string | ButtonType; - rootDoc: Doc; + Document: Doc; label: any; icon: IconProp; color: string; backgroundColor: string; -}
\ No newline at end of file +} diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index ba5370360..8f6550663 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -13,7 +13,7 @@ import { SelectionManager } from '../../../util/SelectionManager'; import { SettingsManager } from '../../../util/SettingsManager'; import { undoable, UndoManager } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; -import { DocComponent } from '../../DocComponent'; +import { ViewBoxBaseComponent } from '../../DocComponent'; import { EditableView } from '../../EditableView'; import { SelectedDocView } from '../../selectedDoc'; import { StyleProp } from '../../StyleProvider'; @@ -41,7 +41,7 @@ export interface ButtonProps extends FieldViewProps { type?: ButtonType; } @observer -export class FontIconBox extends DocComponent<ButtonProps>() { +export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FontIconBox, fieldKey); } @@ -130,7 +130,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { type = 'slider'; break; } - const numScript = (value?: number) => ScriptCast(this.Document.script).script.run({ this: this.Document, value, _readOnly_: value === undefined }); + const numScript = (value?: number) => ScriptCast(this.Document.script).script.run({ this: this.Document, self: this.Document, value, _readOnly_: value === undefined }); const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color); // Script for checking the outcome of the toggle const checkResult = Number(Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3))); @@ -157,7 +157,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { this, e, (e: PointerEvent) => { - return ScriptCast(this.Document.onDragScript)?.script.run({ this: this.Document, value: { doc: value, e } }).result; + return ScriptCast(this.Document.onDragScript)?.script.run({ this: this.Document, self: this.Document, value: { doc: value, e } }).result; }, emptyFunction, emptyFunction @@ -205,7 +205,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { } noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Carousel3D, CollectionViewType.Stacking, CollectionViewType.NoteTaking]; } else { - text = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result; + text = script?.script.run({ this: this.Document, self: this.Document, value: '', _readOnly_: true }).result; // text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); getStyle = (val: string) => ({ fontFamily: val }); } @@ -223,7 +223,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { return ( <Dropdown selectedVal={text} - setSelectedVal={undoable(value => script.script.run({ this: this.Document, value }), `dropdown select ${this.label}`)} + setSelectedVal={undoable(value => script.script.run({ this: this.Document, self: this.Document, value }), `dropdown select ${this.label}`)} color={SettingsManager.userColor} background={SettingsManager.userVariantColor} type={Type.TERT} @@ -247,17 +247,17 @@ export class FontIconBox extends DocComponent<ButtonProps>() { */ @computed get colorButton() { const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color); - const curColor = this.colorScript?.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result ?? 'transparent'; + const curColor = this.colorScript?.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result ?? 'transparent'; const tooltip: string = StrCast(this.Document.toolTip); return ( <ColorPicker setSelectedColor={value => { if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`); - this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false }); + this.colorScript?.script.run({ this: this.Document, self: this.Document, value: value, _readOnly_: false }); }} setFinalColor={value => { - this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false }); + this.colorScript?.script.run({ this: this.Document, self: this.Document, value: value, _readOnly_: false }); this.colorBatch?.end(); this.colorBatch = undefined; }} @@ -277,7 +277,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { const tooltip: string = StrCast(this.Document.toolTip); const script = ScriptCast(this.Document.onClick); - const toggleStatus = script ? script.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result : false; + const toggleStatus = script ? script.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result : false; // Colors const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color); const items = DocListCast(this.dataDoc.data); @@ -308,7 +308,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { const tooltip = StrCast(this.Document.toolTip); const script = ScriptCast(this.Document.onClick); - const toggleStatus = script ? script.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result : false; + const toggleStatus = script ? script.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result : false; // Colors const color = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor); @@ -324,7 +324,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { //background={SettingsManager.userBackgroundColor} icon={this.Icon(color)!} label={this.label} - onPointerDown={() => script.script.run({ this: this.Document, value: !toggleStatus, _readOnly_: false })} + onPointerDown={() => script.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false })} /> ); } @@ -344,9 +344,9 @@ export class FontIconBox extends DocComponent<ButtonProps>() { // Script for running the toggle const script = ScriptCast(this.Document.script); // Function to run the script - const checkResult = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result; + const checkResult = script?.script.run({ this: this.Document, self: this.Document, value: '', _readOnly_: true }).result; - const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ this: this.Document, value, _readOnly_: false }).result; + const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ this: this.Document, self: this.Document, value, _readOnly_: false }).result; return <EditableText editing={false} setEditing={(editing: boolean) => {}} />; @@ -354,7 +354,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { <div className="menuButton editableText"> <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'lock'} /> <div style={{ width: 'calc(100% - .875em)', paddingLeft: '4px' }}> - <EditableView GetValue={() => script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} /> + <EditableView GetValue={() => script?.script.run({ this: this.Document, self: this.Document, value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} /> </div> </div> ); diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx index daf6cd9a6..5ed5fa8fd 100644 --- a/src/client/views/nodes/FunctionPlotBox.tsx +++ b/src/client/views/nodes/FunctionPlotBox.tsx @@ -43,11 +43,8 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps> ); } getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { - const anchor = Docs.Create.ConfigDocument({ - // - annotationOn: this.rootDoc, - }); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), datarange: true } }, this.rootDoc); + const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document }); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), datarange: true } }, this.Document); anchor.config_xRange = new List<number>(Array.from(this._plot.options.xAxis.domain)); anchor.config_yRange = new List<number>(Array.from(this._plot.options.yAxis.domain)); if (addAsAnnotation) this.addDocument(anchor); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 19e393968..d28d71fe3 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -89,26 +89,26 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp const anchor = visibleAnchor ?? Docs.Create.ConfigDocument({ - title: 'ImgAnchor:' + this.rootDoc.title, + title: 'ImgAnchor:' + this.Document.title, config_panX: NumCast(this.layoutDoc._freeform_panX), config_panY: NumCast(this.layoutDoc._freeform_panY), config_viewScale: Cast(this.layoutDoc._freeform_scale, 'number', null), - annotationOn: this.rootDoc, + annotationOn: this.Document, }); if (anchor) { if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; addAsAnnotation && this.addDocument(anchor); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: visibleAnchor ? false : true } }, this.rootDoc); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: visibleAnchor ? false : true } }, this.Document); return anchor; } - return this.rootDoc; + return this.Document; }; componentDidMount() { this._disposers.sizer = reaction( () => ({ forceFull: this.props.renderDepth < 1 || this.layoutDoc._showFullRes, - scrSize: (this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth) * NumCast(this.rootDoc._freeform_scale, 1), + scrSize: (this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth) * NumCast(this.layoutDoc._freeform_scale, 1), selected: this.props.isSelected(), }), ({ forceFull, scrSize, selected }) => (this._curSuffix = selected ? '_o' : this.fieldKey === 'icon' ? '_m' : forceFull ? '_o' : scrSize < 0.25 ? '_s' : scrSize < 0.5 ? '_m' : scrSize < 0.8 ? '_l' : '_o'), @@ -152,7 +152,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp }; if (de.metaKey || targetIsBullseye(e.target as HTMLElement)) { added = de.complete.docDragData.droppedDocuments.reduce((last: boolean, drop: Doc) => { - this.rootDoc[this.fieldKey + '_usePath'] = 'alternate:hover'; + this.layoutDoc[this.fieldKey + '_usePath'] = 'alternate:hover'; return last && Doc.AddDocToList(this.dataDoc, this.fieldKey + '-alternates', drop); }, true); } else if (de.altKey || !this.dataDoc[this.fieldKey]) { @@ -177,13 +177,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp @undoBatch setNativeSize = action(() => { - const scaling = (this.props.DocumentView?.().props.ScreenToLocalTransform().Scale || 1) / NumCast(this.rootDoc._freeform_scale, 1); + const scaling = (this.props.DocumentView?.().props.ScreenToLocalTransform().Scale || 1) / NumCast(this.layoutDoc._freeform_scale, 1); const nscale = NumCast(this.props.PanelWidth()) / scaling; const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']); this.dataDoc[this.fieldKey + '_nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']) * nw; this.dataDoc[this.fieldKey + '_nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']) * nw; - this.rootDoc._freeform_panX = nw * NumCast(this.rootDoc._freeform_panX); - this.rootDoc._freeform_panY = nw * NumCast(this.rootDoc._freeform_panY); + this.layoutDoc._freeform_panX = nw * NumCast(this.layoutDoc._freeform_panX); + this.layoutDoc._freeform_panY = nw * NumCast(this.layoutDoc._freeform_panY); this.dataDoc._freeform_panXMax = this.dataDoc._freeform_panXMax ? nw * NumCast(this.dataDoc._freeform_panXMax) : undefined; this.dataDoc._freeform_panXMin = this.dataDoc._freeform_panXMin ? nw * NumCast(this.dataDoc._freeform_panXMin) : undefined; this.dataDoc._freeform_panYMax = this.dataDoc._freeform_panYMax ? nw * NumCast(this.dataDoc._freeform_panYMax) : undefined; @@ -206,17 +206,17 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp if (!region) return; const cropping = Doc.MakeCopy(region, true); Doc.GetProto(region).lockedPosition = true; - Doc.GetProto(region).title = 'region:' + this.rootDoc.title; + Doc.GetProto(region).title = 'region:' + this.Document.title; Doc.GetProto(region).followLinkToggle = true; this.addDocument(region); const anchx = NumCast(cropping.x); const anchy = NumCast(cropping.y); const anchw = NumCast(cropping._width); const anchh = NumCast(cropping._height); - const viewScale = NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']) / anchw; - cropping.title = 'crop: ' + this.rootDoc.title; - cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width); - cropping.y = NumCast(this.rootDoc.y); + const viewScale = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']) / anchw; + cropping.title = 'crop: ' + this.Document.title; + cropping.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width); + cropping.y = NumCast(this.Document.y); cropping._width = anchw * (this.props.NativeDimScaling?.() || 1); cropping._height = anchh * (this.props.NativeDimScaling?.() || 1); cropping.onClick = undefined; @@ -224,10 +224,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp croppingProto.annotationOn = undefined; croppingProto.isDataDoc = true; croppingProto.backgroundColor = undefined; - croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO + croppingProto.proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO croppingProto.type = DocumentType.IMG; croppingProto.layout = ImageBox.LayoutString('data'); - croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField); + croppingProto.data = ObjectField.MakeCopy(this.dataDoc[this.fieldKey] as ObjectField); croppingProto['data_nativeWidth'] = anchw; croppingProto['data_nativeHeight'] = anchh; croppingProto.freeform_scale = viewScale; @@ -240,8 +240,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp croppingProto.freeform_panY_max = anchh / viewScale; if (addCrop) { DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' }); - cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width); - cropping.y = NumCast(this.rootDoc.y); + cropping.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width); + cropping.y = NumCast(this.Document.y); this.props.addDocTab(cropping, OpenWhere.inParent); } DocumentManager.Instance.AddViewRenderedCb(cropping, dv => setTimeout(() => (dv.ComponentView as ImageBox).setNativeSize(), 200)); @@ -263,7 +263,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp ImageBox.setImageEditorOpen(true); ImageBox.setImageEditorSource(this.choosePath(field.url)); ImageBox.addDoc = this.props.addDocument; - ImageBox.imageRootDoc = this.rootDoc; + ImageBox.imageRootDoc = this.Document; }), icon: 'pencil-alt', }); @@ -319,7 +319,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return `/assets/unknown-file-icon-hi.png`; const ext = extname(url.href); - return url.href.replace(ext, this._curSuffix + ext); + return url.href.replace(ext, (this._error ? '_o' : this._curSuffix) + ext); } considerGooglePhotosLink = () => { @@ -332,7 +332,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp return !tags ? null : <img id={'google-tags'} src={'/assets/google_tags.png'} />; }; - getScrollHeight = () => (this.props.layout_fitWidth?.(this.rootDoc) !== false && NumCast(this.rootDoc._freeform_scale, 1) === NumCast(this.rootDoc._freeform_scaleMin, 1) ? this.nativeSize.nativeHeight : undefined); + getScrollHeight = () => (this.props.layout_fitWidth?.(this.Document) !== false && NumCast(this.layoutDoc._freeform_scale, 1) === NumCast(this.dataDoc._freeform_scaleMin, 1) ? this.nativeSize.nativeHeight : undefined); @computed private get considerDownloadIcon() { @@ -384,7 +384,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp return { nativeWidth, nativeHeight, nativeOrientation }; } @computed get overlayImageIcon() { - const usePath = this.rootDoc[`_${this.fieldKey}_usePath`]; + const usePath = this.layoutDoc[`_${this.fieldKey}_usePath`]; return ( <Tooltip title={ @@ -405,7 +405,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp <div className="imageBox-alternateDropTarget" ref={this._overlayIconRef} - onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.rootDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))} + onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.layoutDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))} style={{ display: (this.props.isContentActive() !== false && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none', width: 'min(10%, 25px)', @@ -430,11 +430,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp return paths.length ? paths : [Utils.CorsProxy('https://cs.brown.edu/~bcz/noImage.png')]; } + @observable _error = ''; + @observable _isHovering = false; // flag to switch between primary and alternate images on hover @computed get content() { TraceMobx(); - const backColor = DashColor(this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor)); + const backColor = DashColor(this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor)); const backAlpha = backColor.red() === 0 && backColor.green() === 0 && backColor.blue() === 0 ? backColor.alpha() : 1; const srcpath = this.layoutDoc.hideImage ? '' : this.paths[0]; const fadepath = this.layoutDoc.hideImage ? '' : this.paths.lastElement(); @@ -452,12 +454,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp transformOrigin = 'right top'; transform = `translate(-100%, 0%) rotate(${rotation}deg) scale(${aspect})`; } - const usePath = this.rootDoc[`_${this.fieldKey}_usePath`]; + const usePath = this.layoutDoc[`_${this.fieldKey}_usePath`]; return ( <div className="imageBox-cont" onPointerEnter={action(() => (this._isHovering = true))} onPointerLeave={action(() => (this._isHovering = false))} key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}> <div className="imageBox-fader" style={{ opacity: backAlpha }}> - <img key="paths" src={srcpath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} /> + <img key="paths" src={srcpath} style={{ transform, transformOrigin }} onError={action(e => (this._error = e.toString()))} draggable={false} width={nativeWidth} /> {fadepath === srcpath ? null : ( <div className={`imageBox-fadeBlocker${(this._isHovering && usePath === 'alternate:hover') || usePath === 'alternate' ? '-hover' : ''}`} style={{ transition: StrCast(this.layoutDoc.viewTransition, 'opacity 1000ms') }}> <img className="imageBox-fadeaway" key="fadeaway" src={fadepath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} /> @@ -481,7 +483,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp } screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop) * this.props.ScreenToLocalTransform().Scale); marqueeDown = (e: React.PointerEvent) => { - if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._freeform_scale, 1) <= NumCast(this.rootDoc.freeform_scaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { + if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) <= NumCast(this.dataDoc.freeform_scaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { setupMoveUpEvents( this, e, @@ -529,7 +531,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp height: this.props.PanelWidth() ? undefined : `100%`, pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined, borderRadius, - overflow: this.layoutDoc.layout_fitWidth || this.props.layout_fitWidth?.(this.rootDoc) ? 'auto' : undefined, + overflow: this.layoutDoc.layout_fitWidth || this.props.layout_fitWidth?.(this.Document) ? 'auto' : undefined, }}> <CollectionFreeFormView ref={this._ffref} diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 95d2a2667..0c6377c3a 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -41,10 +41,9 @@ export class KeyValueBox extends React.Component<FieldViewProps> { componentDidMount() { this.props.setContentView?.(this); } - ignoreNativeDimScaling = returnTrue; + isKeyValueBox = returnTrue; able = returnAlways; layout_fitWidth = returnTrue; - overridePointerEvents = returnAll; onClickScriptDisable = returnAlways; @observable private rows: KeyValuePair[] = []; diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss index 57d36932e..c29af7817 100644 --- a/src/client/views/nodes/KeyValuePair.scss +++ b/src/client/views/nodes/KeyValuePair.scss @@ -26,6 +26,7 @@ position: relative; overflow: auto; display: inline; + align-self: center; } } } diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 6f99b7c28..577685636 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -58,14 +58,12 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> { render() { const props: FieldViewProps = { Document: this.props.doc, - DataDoc: this.props.doc, childFilters: returnEmptyFilter, childFiltersByRanges: returnEmptyFilter, searchFilterDocs: returnEmptyDoclist, styleProvider: DefaultStyleProvider, docViewPath: returnEmptyDoclist, fieldKey: this.props.keyName, - rootSelected: returnFalse, isSelected: returnFalse, setHeight: returnFalse, select: emptyFunction, diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 0d7b2b0a4..e1421878b 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -36,7 +36,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp this._timeout && clearTimeout(this._timeout); } - getTitle() { + @computed get Title() { return this.dataDoc.title_custom ? StrCast(this.Document.title) : this.props.label ? this.props.label : typeof this.dataDoc[this.fieldKey] === 'string' ? StrCast(this.dataDoc[this.fieldKey]) : StrCast(this.Document.title); } @@ -119,7 +119,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp const params = Cast(this.paramsDoc['onClick-paramFieldKeys'], listSpec('string'), []); const missingParams = params?.filter(p => !this.paramsDoc[p]); params?.map(p => DocListCast(this.paramsDoc[p])); // bcz: really hacky form of prefetching ... - const label = this.getTitle(); + const label = this.Title; return ( <div className="labelBox-outerDiv" diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 30cd65cb4..743075c89 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -35,8 +35,8 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { if (this.layoutDoc._layout_isSvg && this.anchor1 && this.anchor2 && this.anchor1.CollectionFreeFormView) { const a_invXf = this.anchor1.props.ScreenToLocalTransform().inverse(); const b_invXf = this.anchor2.props.ScreenToLocalTransform().inverse(); - const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(NumCast(this.anchor1.rootDoc._width), NumCast(this.anchor1.rootDoc._height)) }; - const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(NumCast(this.anchor2.rootDoc._width), NumCast(this.anchor2.rootDoc._height)) }; + const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(NumCast(this.anchor1.Document._width), NumCast(this.anchor1.Document._height)) }; + const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(NumCast(this.anchor2.Document._width), NumCast(this.anchor2.Document._height)) }; const pts = [] as number[][]; pts.push([(a_scrBds.tl[0] + a_scrBds.br[0]) / 2, (a_scrBds.tl[1] + a_scrBds.br[1]) / 2]); @@ -65,8 +65,8 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { const this_xf = parxf?.screenToLocalXf ?? Transform.Identity; //this.props.ScreenToLocalTransform(); const a_invXf = a.props.ScreenToLocalTransform().inverse(); const b_invXf = b.props.ScreenToLocalTransform().inverse(); - const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(NumCast(a.rootDoc._width), NumCast(a.rootDoc._height)) }; - const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(NumCast(b.rootDoc._width), NumCast(b.rootDoc._height)) }; + const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(NumCast(a.Document._width), NumCast(a.Document._height)) }; + const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(NumCast(b.Document._width), NumCast(b.Document._height)) }; const a_bds = { tl: this_xf.transformPoint(a_scrBds.tl[0], a_scrBds.tl[1]), br: this_xf.transformPoint(a_scrBds.br[0], a_scrBds.br[1]) }; const b_bds = { tl: this_xf.transformPoint(b_scrBds.tl[0], b_scrBds.tl[1]), br: this_xf.transformPoint(b_scrBds.br[0], b_scrBds.br[1]) }; diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 7e4f1da8e..ad5324b3e 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -260,7 +260,6 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { }} Document={this._targetDoc!} moveDocument={returnFalse} - rootSelected={returnFalse} styleProvider={this.props.docProps?.styleProvider} docViewPath={returnEmptyDoclist} ScreenToLocalTransform={Transform.Identity} diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index 4bb0f14d2..e554cb8ad 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -57,28 +57,29 @@ export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { _timer: any; @observable progress = ''; componentDidMount() { - if (!LoadingBox.CurrentlyLoading?.includes(this.rootDoc)) { - this.rootDoc.loadingError = 'Upload interrupted, please try again'; + if (!LoadingBox.CurrentlyLoading?.includes(this.Document)) { + this.Document.loadingError = 'Upload interrupted, please try again'; } else { const updateFunc = async () => { - const result = await Networking.QueryYoutubeProgress(StrCast(this.rootDoc[Id])); // We use the guid of the overwriteDoc to track file uploads. + const result = await Networking.QueryYoutubeProgress(StrCast(this.Document[Id])); // We use the guid of the overwriteDoc to track file uploads. runInAction(() => (this.progress = result.progress)); - !this.rootDoc.loadingError && (this._timer = setTimeout(updateFunc, 1000)); + !this.Document.loadingError && this._timer && (this._timer = setTimeout(updateFunc, 1000)); }; this._timer = setTimeout(updateFunc, 1000); } } componentWillUnmount() { clearTimeout(this._timer); + this._timer = undefined; } render() { return ( - <div className="loadingBoxContainer" style={{ background: !this.rootDoc.loadingError ? '' : 'red' }}> + <div className="loadingBoxContainer" style={{ background: !this.Document.loadingError ? '' : 'red' }}> <div className="loadingBox-textContainer"> - <span className="loadingBox-title">{StrCast(this.rootDoc.title)}</span> - <p className="loadingBox-headerText">{StrCast(this.rootDoc.loadingError, 'Loading ' + (this.progress.replace('[download]', '') || '(can take several minutes)'))}</p> - {this.rootDoc.loadingError ? null : ( + <span className="loadingBox-title">{StrCast(this.Document.title)}</span> + <p className="loadingBox-headerText">{StrCast(this.Document.loadingError, 'Loading ' + (this.progress.replace('[download]', '') || '(can take several minutes)'))}</p> + {this.Document.loadingError ? null : ( <div className="loadingBox-spinner"> <ReactLoading type="spinningBubbles" color="blue" height={100} width={100} /> </div> diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 9b75ca7e3..398d1255e 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -195,7 +195,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps title="Toggle Sidebar" style={{ display: !this.props.isContentActive() ? 'none' : undefined, - top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5, + top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5, backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK, }} onPointerDown={this.sidebarBtnDown}> @@ -227,8 +227,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps }); const targetCreator = (annotationOn: Doc | undefined) => { - const target = DocUtils.GetNewTextDoc('Note linked to ' + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow'); - FormattedTextBox.SelectOnLoad = target[Id]; + const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow'); + FormattedTextBox.SetSelectOnLoad(target); return target; }; const docView = this.props.DocumentView?.(); @@ -321,7 +321,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps }; @observable - bingSearchBarContents: any = this.rootDoc.map; // For Bing Maps: The contents of the Bing search bar (string) + bingSearchBarContents: any = this.dataDoc.map; // For Bing Maps: The contents of the Bing search bar (string) geoDataRequestOptions = { entityType: 'PopulatedPlace', @@ -354,9 +354,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps deselectPin = () => { if (this.selectedPin) { // Removes filter - Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove'); - Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove'); - Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove'); + Doc.setDocFilter(this.layoutDoc, 'latitude', this.selectedPin.latitude, 'remove'); + Doc.setDocFilter(this.layoutDoc, 'longitude', this.selectedPin.longitude, 'remove'); + Doc.setDocFilter(this.layoutDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove'); const temp = this.selectedPin; if (!this._unmounting) { @@ -369,7 +369,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps } this.map_docToPinMap.set(temp, newpin); this.selectedPin = undefined; - this.bingSearchBarContents = this.rootDoc.map; + this.bingSearchBarContents = this.dataDoc.map; } }; @@ -386,9 +386,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.selectedPin = pinDoc; this.bingSearchBarContents = pinDoc.map; - // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match'); - // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match'); - Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPin)}`, 'check'); + // Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'match'); + // Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'match'); + Doc.setDocFilter(this.layoutDoc, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPin)}`, 'check'); this.recolorPin(this.selectedPin, 'green'); @@ -457,8 +457,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps, existingPin?: Doc) => { /// this should use SELECTED pushpin for lat/long if there is a selection, otherwise CENTER const anchor = Docs.Create.ConfigDocument({ - title: 'MapAnchor:' + this.rootDoc.title, - text: StrCast(this.selectedPin?.map) || StrCast(this.rootDoc.map) || 'map location', + title: 'MapAnchor:' + this.Document.title, + text: StrCast(this.selectedPin?.map) || StrCast(this.dataDoc.map) || 'map location', config_latitude: NumCast((existingPin ?? this.selectedPin)?.latitude ?? this.dataDoc.latitude), config_longitude: NumCast((existingPin ?? this.selectedPin)?.longitude ?? this.dataDoc.longitude), config_map_zoom: NumCast(this.dataDoc.map_zoom), @@ -466,15 +466,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps config_map: StrCast((existingPin ?? this.selectedPin)?.map) || StrCast(this.dataDoc.map), layout_unrendered: true, mapPin: existingPin ?? this.selectedPin, - annotationOn: this.rootDoc, + annotationOn: this.Document, }); if (anchor) { if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; addAsAnnotation && this.addDocument(anchor); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.rootDoc); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.Document); return anchor; } - return this.rootDoc; + return this.Document; }; map_docToPinMap = new Map<Doc, any>(); @@ -521,9 +521,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps deleteSelectedPin = undoable(() => { if (this.selectedPin) { // Removes filter - Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove'); - Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove'); - Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove'); + Doc.setDocFilter(this.layoutDoc, 'latitude', this.selectedPin.latitude, 'remove'); + Doc.setDocFilter(this.layoutDoc, 'longitude', this.selectedPin.longitude, 'remove'); + Doc.setDocFilter(this.layoutDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove'); this.removePushpin(this.selectedPin); } @@ -611,7 +611,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, 'Map ViewType Change')); this._disposers.mapLocation = reaction( - () => this.rootDoc.map, + () => this.dataDoc.map, mapLoc => (this.bingSearchBarContents = mapLoc), { fireImmediately: true } ); @@ -636,7 +636,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps ); this._disposers.location = reaction( - () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.map_zoom, mapType: this.rootDoc.map_type }), + () => ({ lat: this.dataDoc.latitude, lng: this.dataDoc.longitude, zoom: this.dataDoc.map_zoom, mapType: this.dataDoc.map_type }), locationObject => { // if (this._bingMap.current) try { @@ -687,7 +687,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps } }, e => { - const createPin = () => this.createPushpin(this.rootDoc.latitude, this.rootDoc.longitude, this.rootDoc.map); + const createPin = () => this.createPushpin(this.dataDoc.latitude, this.dataDoc.longitude, this.dataDoc.map); if (this.bingSearchBarContents) { this.bingSearch().then(createPin); } else createPin(); @@ -703,7 +703,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps render() { // bcz: no idea what's going on here, but bings maps have some kind of bug // such that we need to delay rendering a second map on startup until the first map is rendered. - this.rootDoc[DocCss]; + this.Document[DocCss]; if (MapBox._rerenderDelay) { // prettier-ignore this._rerenderTimeout = this._rerenderTimeout ?? @@ -712,7 +712,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps MapBox._rerenderDelay = 0; } this._rerenderTimeout = undefined; - this.rootDoc[DocCss] = this.rootDoc[DocCss] + 1; + this.Document[DocCss] = this.Document[DocCss] + 1; }), MapBox._rerenderDelay); return null; } @@ -774,7 +774,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps {...this.props} renderDepth={this.props.renderDepth + 1} Document={pushpin} - DataDoc={undefined} + TemplateDataDocument={undefined} PanelWidth={returnOne} PanelHeight={returnOne} NativeWidth={returnOne} @@ -812,7 +812,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps ref={this._sidebarRef} {...this.props} fieldKey={this.fieldKey} - rootDoc={this.rootDoc} + Document={this.Document} layoutDoc={this.layoutDoc} dataDoc={this.dataDoc} usePanelWidth={true} diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx index 6bad7d724..a77bfc50a 100644 --- a/src/client/views/nodes/MapBox/MapBox2.tsx +++ b/src/client/views/nodes/MapBox/MapBox2.tsx @@ -445,7 +445,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps title="Toggle Sidebar" style={{ display: !this.props.isContentActive() ? 'none' : undefined, - top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5, + top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5, backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK, }} onPointerDown={this.sidebarBtnDown}> @@ -495,7 +495,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps ); } - getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc; + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.Document; /** * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker @@ -577,7 +577,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps ref={this._sidebarRef} {...this.props} fieldKey={this.fieldKey} - rootDoc={this.rootDoc} + Document={this.Document} layoutDoc={this.layoutDoc} dataDoc={this.dataDoc} usePanelWidth={true} diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx index 66c47d131..415a9d776 100644 --- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx +++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx @@ -32,9 +32,9 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi addNoteClick = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => { - const newBox = Docs.Create.TextDocument('Note', { _layout_autoHeight: true }); - FormattedTextBox.SelectOnLoad = newBox[Id]; // track the new text box so we can give it a prop that tells it to focus itself when it's displayed - Doc.AddDocToList(this.props.place, 'data', newBox); + const newDoc = Docs.Create.TextDocument('Note', { _layout_autoHeight: true }); + FormattedTextBox.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed + Doc.AddDocToList(this.props.place, 'data', newDoc); this._stack?.scrollToBottom(); e.stopPropagation(); e.preventDefault(); @@ -47,9 +47,9 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean); render() { return ( - <InfoWindow - // anchor={this.props.markerMap[this.props.place[Id]]} - onCloseClick={this.handleInfoWindowClose}> + <InfoWindow + // anchor={this.props.markerMap[this.props.place[Id]]} + onCloseClick={this.handleInfoWindowClose}> <div className="mapbox-infowindow"> <div style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}> <CollectionStackingView @@ -57,7 +57,7 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi {...this.props} setContentView={emptyFunction} Document={this.props.place} - DataDoc={undefined} + TemplateDataDocument={undefined} fieldKey="data" NativeWidth={returnZero} NativeHeight={returnZero} @@ -68,9 +68,8 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi NativeDimScaling={returnOne} isContentActive={returnTrue} chromeHidden={true} - rootSelected={returnFalse} - childHideResizeHandles={returnTrue} - childHideDecorationTitle={returnTrue} + childHideResizeHandles={true} + childHideDecorationTitle={true} childLayoutFitWidth={this.childLayoutFitWidth} // childDocumentsActive={returnFalse} removeDocument={this.removeDoc} diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 1035116d5..9787de0ab 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -97,7 +97,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps if (!region) return; const cropping = Doc.MakeCopy(region, true); Doc.GetProto(region).lockedPosition = true; - Doc.GetProto(region).title = 'region:' + this.rootDoc.title; + Doc.GetProto(region).title = 'region:' + this.Document.title; Doc.GetProto(region).followLinkToggle = true; this.addDocument(region); @@ -113,16 +113,16 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps const anchw = NumCast(cropping._width) * (this.props.NativeDimScaling?.() || 1); const anchh = NumCast(cropping._height) * (this.props.NativeDimScaling?.() || 1); const viewScale = 1; - cropping.title = 'crop: ' + this.rootDoc.title; - cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width); - cropping.y = NumCast(this.rootDoc.y); + cropping.title = 'crop: ' + this.Document.title; + cropping.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width); + cropping.y = NumCast(this.Document.y); cropping._width = anchw; cropping._height = anchh; cropping.onClick = undefined; const croppingProto = Doc.GetProto(cropping); croppingProto.annotationOn = undefined; croppingProto.isDataDoc = true; - croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO + croppingProto.proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO croppingProto.type = DocumentType.IMG; croppingProto.layout = ImageBox.LayoutString('data'); croppingProto.data = new ImageField(Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')); @@ -139,8 +139,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps htmlString, anchw, anchh, - (NumCast(region.y) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']), - (NumCast(region.x) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']), + (NumCast(region.y) * this.props.PanelWidth()) / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']), + (NumCast(region.x) * this.props.PanelWidth()) / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']), 4 ) .then((data_url: any) => { @@ -173,7 +173,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.props.PanelWidth(), this.props.PanelHeight(), NumCast(this.layoutDoc._layout_scrollTop), - NumCast(this.rootDoc[this.fieldKey + '_nativeHeight'], 1), + NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], 1), true, this.layoutDoc[Id] + '-icon', (iconFile: string, nativeWidth: number, nativeHeight: number) => { @@ -200,13 +200,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps { fireImmediately: true } ); this._disposers.scroll = reaction( - () => this.rootDoc.layout_scrollTop, + () => this.layoutDoc.layout_scrollTop, () => { if (!(ComputedField.WithoutComputed(() => FieldValue(this.props.Document[this.SidebarKey + '_panY'])) instanceof ComputedField)) { this.props.Document[this.SidebarKey + '_panY'] = ComputedField.MakeFunction('this.layout_scrollTop'); } - this.props.Document[this.SidebarKey + '_freeform_scale'] = 1; - this.props.Document[this.SidebarKey + '_freeform_panX'] = 0; + this.layoutDoc[this.SidebarKey + '_freeform_scale'] = 1; + this.layoutDoc[this.SidebarKey + '_freeform_panX'] = 0; } ); } @@ -236,12 +236,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps } const docAnchor = () => Docs.Create.ConfigDocument({ - title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._layout_scrollTop)?.toFixed(0)), - annotationOn: this.rootDoc, + title: StrCast(this.Document.title + '@' + NumCast(this.layoutDoc._layout_scrollTop)?.toFixed(0)), + annotationOn: this.Document, }); const visibleAnchor = this._pdfViewer?._getAnchor?.(this._pdfViewer.savedAnnotations(), true); const anchor = visibleAnchor ?? docAnchor(); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true, pannable: true } }, this.rootDoc); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true, pannable: true } }, this.Document); anchor.text = ele?.textContent ?? ''; anchor.text_html = ele?.innerHTML; addAsAnnotation && this.addDocument(anchor); @@ -434,7 +434,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps return PDFBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1); }; @undoBatch - toggleSidebarType = () => (this.rootDoc[this.SidebarKey + '_type_collection'] = this.rootDoc[this.SidebarKey + '_type_collection'] === CollectionViewType.Freeform ? CollectionViewType.Stacking : CollectionViewType.Freeform); + toggleSidebarType = () => (this.dataDoc[this.SidebarKey + '_type_collection'] = this.dataDoc[this.SidebarKey + '_type_collection'] === CollectionViewType.Freeform ? CollectionViewType.Stacking : CollectionViewType.Freeform); specificContextMenu = (e: React.MouseEvent): void => { const cm = ContextMenu.Instance; const options = cm.findByDescription('Options...'); @@ -473,7 +473,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps title="Toggle Sidebar" style={{ display: !this.props.isContentActive() ? 'none' : undefined, - top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5, + top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5, backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK, }} onPointerDown={e => this.sidebarBtnDown(e, true)}> @@ -509,7 +509,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps <SidebarAnnos ref={this._sidebarRef} {...this.props} - rootDoc={this.rootDoc} + Document={this.Document} layoutDoc={this.layoutDoc} dataDoc={this.dataDoc} setHeight={emptyFunction} diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 224bc6f67..c2e204eab 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -37,7 +37,7 @@ declare class MediaRecorder { // setRaised: (r: { coord: Vector2, off: Vector3 }[]) => void; // x: number; // y: number; -// rootDoc: Doc; +// doc: Doc; // color: string; // } @@ -69,8 +69,8 @@ declare class MediaRecorder { // const normals = new Float32Array(quad_normals); // const uvs = new Float32Array(quad_uvs); // Each vertex has one uv coordinate for texture mapping // const indices = new Uint32Array(quad_indices); // Use the four vertices to draw the two triangles that make up the square. -// const popOut = () => NumCast(this.props.rootDoc.popOut); -// const popOff = () => NumCast(this.props.rootDoc.popOff); +// const popOut = () => NumCast(this.props.Document.popOut); +// const popOff = () => NumCast(this.props.Document.popOff); // return ( // <mesh key={`mesh${topLeft[0]}${topLeft[1]}`} onClick={action(async e => { // this.props.setRaised([ @@ -123,9 +123,9 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl constructor(props: any) { super(props); - if (this.rootDoc.videoWall) { - this.rootDoc.nativeWidth = undefined; - this.rootDoc.nativeHeight = undefined; + if (this.dataDoc.videoWall) { + this.layoutDoc.nativeWidth = undefined; + this.layoutDoc.nativeHeight = undefined; this.layoutDoc.popOff = 0; this.layoutDoc.popOut = 1; } else { @@ -134,7 +134,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl } getAnchor = (addAsAnnotation: boolean) => { const startTime = Cast(this.layoutDoc._layout_currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined); - return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.rootDoc; + return CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.annotationKey, startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.Document; }; videoLoad = () => { @@ -151,7 +151,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl componentDidMount() { this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = 0; this.props.setContentView?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. - // this.rootDoc.videoWall && reaction(() => ({ width: this.props.PanelWidth(), height: this.props.PanelHeight() }), + // this.layoutDoc.videoWall && reaction(() => ({ width: this.props.PanelWidth(), height: this.props.PanelHeight() }), // ({ width, height }) => { // if (this._camera) { // const angle = -Math.abs(1 - width / height); @@ -173,7 +173,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl }; @computed get content() { - if (this.rootDoc.videoWall) return null; + if (this.layoutDoc.videoWall) return null; return ( <video className={'videoBox-content'} @@ -181,7 +181,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl ref={r => { this._videoRef = r; setTimeout(() => { - if (this.rootDoc.mediaState === media_state.PendingRecording && this._videoRef) { + if (this.layoutDoc.mediaState === media_state.PendingRecording && this._videoRef) { this.toggleRecording(); } }, 100); @@ -202,12 +202,12 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl // @observable _raised = [] as { coord: Vector2, off: Vector3 }[]; // @action setRaised = (r: { coord: Vector2, off: Vector3 }[]) => this._raised = r; @computed get threed() { - // if (this.rootDoc.videoWall) { + // if (this.layoutDoc.videoWall) { // const screens: any[] = []; // const colors = ["yellow", "red", "orange", "brown", "maroon", "gray"]; // let count = 0; // numberRange(this._numScreens).forEach(x => numberRange(this._numScreens).forEach(y => screens.push( - // <VideoTile rootDoc={this.rootDoc} color={colors[count++ % colors.length]} x={x} y={y} raised={this._raised} setRaised={this.setRaised} />))); + // <VideoTile doc={this.layoutDoc} color={colors[count++ % colors.length]} x={x} y={y} raised={this._raised} setRaised={this.setRaised} />))); // return <Canvas key="canvas" id="CANCAN" style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }} gl={{ antialias: false }} colorManagement={false} onCreated={props => { // this._camera = props.camera; // props.camera.position.set(this._numScreens / 2, this._numScreens / 2, this._numScreens - 2); @@ -250,7 +250,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl this.dataDoc[this.fieldKey + '_presentation'] = JSON.stringify(presCopy); } TrackMovements.Instance.finish(); - const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() }); + const file = new File(vid_chunks, `${this.Document[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() }); const [{ result }] = await Networking.UploadFilesToServer({ file }); this.dataDoc[this.fieldKey + '_duration'] = (new Date().getTime() - this.recordingStart!) / 1000; if (!(result instanceof Error)) { @@ -279,13 +279,13 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl const ind = DocUtils.ActiveRecordings.indexOf(this); ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1); - CaptureManager.Instance.open(this.rootDoc); + CaptureManager.Instance.open(this.Document); } }; setupDictation = () => { if (this.dataDoc[this.fieldKey + '_dictation']) return; - const dictationText = DocUtils.GetNewTextDoc('dictation', NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height)); + const dictationText = DocUtils.GetNewTextDoc('dictation', NumCast(this.Document.x), NumCast(this.Document.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height)); const textField = Doc.LayoutFieldKey(dictationText); dictationText._layout_autoHeight = false; const dictationTextProto = Doc.GetProto(dictationText); diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index a51a83b26..127edaed7 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -61,7 +61,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable constructor(props: any) { super(props); if (!this.compileParams.length) { - const params = ScriptCast(this.rootDoc[this.props.fieldKey])?.script.options.params as { [key: string]: any }; + const params = ScriptCast(this.dataDoc[this.props.fieldKey])?.script.options.params as { [key: string]: any }; if (params) { this.compileParams = Array.from(Object.keys(params)) .filter(p => !p.startsWith('_')) @@ -78,30 +78,30 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable return this.compileParams.map(p => p.split(':')[1].trim()); } @computed({ keepAlive: true }) get rawScript() { - return ScriptCast(this.rootDoc[this.fieldKey])?.script.originalScript ?? ''; + return ScriptCast(this.dataDoc[this.fieldKey])?.script.originalScript ?? ''; } @computed({ keepAlive: true }) get functionName() { - return StrCast(this.rootDoc[this.props.fieldKey + '-functionName'], ''); + return StrCast(this.dataDoc[this.fieldKey + '-functionName'], ''); } @computed({ keepAlive: true }) get functionDescription() { - return StrCast(this.rootDoc[this.props.fieldKey + '-functionDescription'], ''); + return StrCast(this.dataDoc[this.fieldKey + '-functionDescription'], ''); } @computed({ keepAlive: true }) get compileParams() { - return Cast(this.rootDoc[this.props.fieldKey + '-params'], listSpec('string'), []); + return Cast(this.dataDoc[this.fieldKey + '-params'], listSpec('string'), []); } set rawScript(value) { - Doc.SetInPlace(this.rootDoc, this.props.fieldKey, new ScriptField(undefined, undefined, value), true); + this.dataDoc[this.fieldKey] = new ScriptField(undefined, undefined, value); } set functionName(value) { - Doc.SetInPlace(this.rootDoc, this.props.fieldKey + '-functionName', value, true); + this.dataDoc[this.fieldKey + '-functionName'] = value; } set functionDescription(value) { - Doc.SetInPlace(this.rootDoc, this.props.fieldKey + '-functionDescription', value, true); + this.dataDoc[this.fieldKey + '-functionDescription'] = value; } set compileParams(value) { - Doc.SetInPlace(this.rootDoc, this.props.fieldKey + '-params', new List<string>(value), true); + this.dataDoc[this.fieldKey + '-params'] = new List<string>(value); } getValue(result: any, descrip: boolean) { @@ -165,9 +165,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable // only included in buttons, transforms scripting UI to a button @action - onFinish = () => { - this.rootDoc.layout_fieldKey = 'layout'; - }; + onFinish = () => (this.layoutDoc.layout_fieldKey = 'layout'); // displays error message @action @@ -189,7 +187,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable params, typecheck: false, }); - Doc.SetInPlace(this.rootDoc, this.fieldKey, result.compiled ? new ScriptField(result, undefined, this.rawText) : undefined, true); + this.dataDoc[this.fieldKey] = result.compiled ? new ScriptField(result, undefined, this.rawText) : undefined; this.onError(result.compiled ? undefined : result.errors); return result.compiled; }; @@ -199,9 +197,9 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable onRun = () => { if (this.onCompile()) { const bindings: { [name: string]: any } = {}; - this.paramsNames.forEach(key => (bindings[key] = this.rootDoc[key])); + this.paramsNames.forEach(key => (bindings[key] = this.dataDoc[key])); // binds vars so user doesnt have to refer to everything as this.<var> - ScriptCast(this.rootDoc[this.fieldKey], null)?.script.run({ ...bindings, self: this.rootDoc, this: this.layoutDoc }, this.onError); + ScriptCast(this.dataDoc[this.fieldKey], null)?.script.run({ ...bindings, this: this.Document }, this.onError); } }; @@ -270,7 +268,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable @action onDrop = (e: Event, de: DragManager.DropEvent, fieldKey: string) => { if (de.complete.docDragData) { - de.complete.docDragData.droppedDocuments.forEach(doc => Doc.SetInPlace(this.rootDoc, fieldKey, doc, true)); + de.complete.docDragData.droppedDocuments.forEach(doc => (this.dataDoc[fieldKey] = doc)); e.stopPropagation(); return true; } @@ -280,7 +278,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable // deletes a param from all areas in which it is stored @action onDelete = (num: number) => { - Doc.SetInPlace(this.rootDoc, this.paramsNames[num], undefined, true); + this.dataDoc[this.paramsNames[num]] = undefined; this.compileParams.splice(num, 1); return true; }; @@ -290,13 +288,13 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable viewChanged = (e: React.ChangeEvent, name: string) => { //@ts-ignore const val = e.target.selectedOptions[0].value; - Doc.SetInPlace(this.rootDoc, name, val[0] === 'S' ? val.substring(1) : val[0] === 'N' ? parseInt(val.substring(1)) : val.substring(1) === 'true', true); + this.dataDoc[name] = val[0] === 'S' ? val.substring(1) : val[0] === 'N' ? parseInt(val.substring(1)) : val.substring(1) === 'true'; }; // creates a copy of the script document onCopy = () => { - const copy = Doc.MakeCopy(this.rootDoc, true); - copy.x = NumCast(this.dataDoc.x) + NumCast(this.dataDoc._width); + const copy = Doc.MakeCopy(this.Document, true); + copy.x = NumCast(this.Document.x) + NumCast(this.dataDoc._width); this.props.addDocument?.(copy); }; @@ -346,8 +344,8 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable maxHeight={72} height={35} fontSize={14} - contents={StrCast(DocCast(this.rootDoc[parameter])?.title, 'undefined')} - GetValue={() => StrCast(DocCast(this.rootDoc[parameter])?.title, 'undefined')} + contents={StrCast(DocCast(this.dataDoc[parameter])?.title, 'undefined')} + GetValue={() => StrCast(DocCast(this.dataDoc[parameter])?.title, 'undefined')} SetValue={action((value: string) => { const script = CompileScript(value, { addReturn: true, @@ -357,7 +355,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable const results = script.compiled && script.run(); if (results && results.success) { this._errorMessage = ''; - Doc.SetInPlace(this.rootDoc, parameter, results.result, true); + this.dataDoc[parameter] = results.result; return true; } this._errorMessage = 'invalid document'; @@ -370,7 +368,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable // rendering when a string's value can be set in applied UI renderBasicType(parameter: string, isNum: boolean) { - const strVal = isNum ? NumCast(this.rootDoc[parameter]).toString() : StrCast(this.rootDoc[parameter]); + const strVal = isNum ? NumCast(this.dataDoc[parameter]).toString() : StrCast(this.dataDoc[parameter]); return ( <div className="scriptingBox-paramInputs" style={{ overflowY: 'hidden' }}> <EditableView @@ -384,7 +382,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable const setValue = isNum ? parseInt(value) : value; if (setValue !== undefined && setValue !== ' ') { this._errorMessage = ''; - Doc.SetInPlace(this.rootDoc, parameter, setValue, true); + this.dataDoc[parameter] = setValue; return true; } this._errorMessage = 'invalid input'; @@ -405,7 +403,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable className="scriptingBox-viewPicker" onPointerDown={e => e.stopPropagation()} onChange={e => this.viewChanged(e, parameter)} - value={typeof this.rootDoc[parameter] === 'string' ? 'S' + StrCast(this.rootDoc[parameter]) : typeof this.rootDoc[parameter] === 'number' ? 'N' + NumCast(this.rootDoc[parameter]) : 'B' + BoolCast(this.rootDoc[parameter])}> + value={typeof this.dataDoc[parameter] === 'string' ? 'S' + StrCast(this.dataDoc[parameter]) : typeof this.dataDoc[parameter] === 'number' ? 'N' + NumCast(this.dataDoc[parameter]) : 'B' + BoolCast(this.dataDoc[parameter])}> {types.map((type, i) => ( <option key={i} className="scriptingBox-viewOption" value={(typeof type === 'string' ? 'S' : typeof type === 'number' ? 'N' : 'B') + type}> {' '} @@ -703,7 +701,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable // toolbar (with compile and apply buttons) for scripting UI renderScriptingTools() { - const buttonStyle = 'scriptingBox-button' + (StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? '-finish' : ''); + const buttonStyle = 'scriptingBox-button' + (StrCast(this.Document.layout_fieldKey).startsWith('layout_on') ? '-finish' : ''); return ( <div className="scriptingBox-toolbar"> <button @@ -731,7 +729,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable Save </button> - {!StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? null : ( // onClick, onChecked, etc need a Finish button to return to their default layout + {!StrCast(this.Document.layout_fieldKey).startsWith('layout_on') ? null : ( // onClick, onChecked, etc need a Finish button to return to their default layout <button className={buttonStyle} onPointerDown={e => { @@ -777,7 +775,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable // toolbar (with edit and run buttons and error message) for params UI renderTools(toolSet: string, func: () => void) { - const buttonStyle = 'scriptingBox-button' + (StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? '-finish' : ''); + const buttonStyle = 'scriptingBox-button' + (StrCast(this.Document.layout_fieldKey).startsWith('layout_on') ? '-finish' : ''); return ( <div className="scriptingBox-toolbar"> <button @@ -796,7 +794,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable }}> {toolSet} </button> - {!StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? null : ( + {!StrCast(this.Document.layout_fieldKey).startsWith('layout_on') ? null : ( <button className={buttonStyle} onPointerDown={e => { diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index f87d94784..0f6c6724a 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -267,7 +267,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp this.player && this._contentRef && this._contentRef.requestFullscreen(); } try { - this._youtubePlayer && this.props.addDocTab(this.rootDoc, OpenWhere.add); + this._youtubePlayer && this.props.addDocTab(this.Document, OpenWhere.add); } catch (e) { console.log('Video FullScreen Exception:', e); } @@ -329,7 +329,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp onClick: FollowLinkScript(), }); this.props.addDocument?.(b); - DocUtils.MakeLink(b, this.rootDoc, { link_relationship: 'video snapshot' }); + DocUtils.MakeLink(b, this.Document, { link_relationship: 'video snapshot' }); Networking.PostToServer('/youtubeScreenshot', { id: this.youtubeVideoId, timecode: this.layoutDoc._layout_currentTimecode, @@ -344,7 +344,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp //convert to desired file format const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png' // if you want to preview the captured image, - const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, ''); + const retitled = StrCast(this.Document.title).replace(/[ -\.:]/g, ''); const encodedFilename = encodeURIComponent('snapshot' + retitled + '_' + (this.layoutDoc._layout_currentTimecode || 0).toString().replace(/\./, '_')); const filename = basename(encodedFilename); Utils.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && (cb ?? this.createSnapshotLink)(returnedFilename, downX, downY)); @@ -388,9 +388,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp const marquee = AnchorMenu.Instance.GetAnchor?.(undefined, addAsAnnotation); if (!addAsAnnotation && marquee) marquee.backgroundColor = 'transparent'; const anchor = addAsAnnotation - ? CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || this.rootDoc - : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.rootDoc }); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.rootDoc); + ? CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.annotationKey, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || this.Document + : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.Document }); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.Document); return anchor; }; @@ -437,7 +437,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp canvas.height = 100; canvas.width = 100; canvas.getContext('2d')?.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, 100, 100); - const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, ''); + const retitled = StrCast(this.Document.title).replace(/[ -\.:]/g, ''); const encodedFilename = encodeURIComponent('thumbnail' + retitled + '_' + video.currentTime.toString().replace(/\./, '_')); thumbnailPromises?.push(Utils.convertDataUri(canvas.toDataURL(), basename(encodedFilename), true)); const newTime = video.currentTime + video.duration / (VideoBox.numThumbnails - 1); @@ -498,7 +498,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp const subitems: ContextMenuProps[] = []; subitems.push({ description: 'Full Screen', event: this.FullScreen, icon: 'expand' }); subitems.push({ description: 'Take Snapshot', event: this.Snapshot, icon: 'expand-arrows-alt' }); - this.rootDoc.type === DocumentType.SCREENSHOT && + this.Document.type === DocumentType.SCREENSHOT && subitems.push({ description: 'Screen Capture', event: async () => { @@ -575,7 +575,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp key="video" autoPlay={this._screenCapture} ref={this.setVideoRef} - style={this._fullScreen ? this.fullScreenSize() : this.isCropped ? { width: 'max-content', height: 'max-content', transform: `scale(${1 / NumCast(this.rootDoc._freeform_scale)})`, transformOrigin: 'top left' } : {}} + style={this._fullScreen ? this.fullScreenSize() : this.isCropped ? { width: 'max-content', height: 'max-content', transform: `scale(${1 / NumCast(this.layoutDoc._freeform_scale)})`, transformOrigin: 'top left' } : {}} onCanPlay={this.videoLoad} controls={VideoBox._nativeControls} onPlay={() => this.Play()} @@ -902,7 +902,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp scaling = () => this.props.NativeDimScaling?.() || 1; panelWidth = () => (this.props.PanelWidth() * this.heightPercent) / 100; - panelHeight = () => (this.layoutDoc._layout_fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : (this.props.PanelHeight() * this.heightPercent) / 100); + panelHeight = () => (this.layoutDoc._layout_fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.dataDoc) || 1) : (this.props.PanelHeight() * this.heightPercent) / 100); screenToLocalTransform = () => { const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling(); @@ -999,7 +999,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp const cropping = Doc.MakeCopy(region, true); Doc.GetProto(region).backgroundColor = 'transparent'; Doc.GetProto(region).lockedPosition = true; - Doc.GetProto(region).title = 'region:' + this.rootDoc.title; + Doc.GetProto(region).title = 'region:' + this.Document.title; Doc.GetProto(region).followLinkToggle = true; region._timecodeToHide = NumCast(region._timecodeToShow) + 0.0001; this.addDocument(region); @@ -1007,10 +1007,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp const anchy = NumCast(cropping.y); const anchw = NumCast(cropping._width); const anchh = NumCast(cropping._height); - const viewScale = NumCast(this.rootDoc[this.fieldKey + '_nativeWidth']) / anchw; - cropping.title = 'crop: ' + this.rootDoc.title; - cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width); - cropping.y = NumCast(this.rootDoc.y); + const viewScale = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']) / anchw; + cropping.title = 'crop: ' + this.Document.title; + cropping.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width); + cropping.y = NumCast(this.Document.y); cropping._width = anchw * (this.props.NativeDimScaling?.() || 1); cropping._height = anchh * (this.props.NativeDimScaling?.() || 1); cropping.timecodeToHide = undefined; @@ -1019,10 +1019,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp const croppingProto = Doc.GetProto(cropping); croppingProto.annotationOn = undefined; croppingProto.isDataDoc = true; - croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO + croppingProto.proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO croppingProto.type = DocumentType.VID; croppingProto.layout = VideoBox.LayoutString('data'); - croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField); + croppingProto.data = ObjectField.MakeCopy(this.dataDoc[this.fieldKey] as ObjectField); croppingProto['data_nativeWidth'] = anchw; croppingProto['data_nativeHeight'] = anchh; croppingProto.videoCrop = true; @@ -1093,7 +1093,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp {!this._mainCont.current || !this._annotationLayer.current ? null : ( <MarqueeAnnotator ref={this._marqueeref} - Document={this.rootDoc} + Document={this.Document} scrollTop={0} annotationLayerScrollTop={0} scaling={returnOne} diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 1e6d92327..b3afe6ba0 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -489,7 +489,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this._scrollHeight = Math.max(this._scrollHeight, iframeContent.body.scrollHeight || 0); if (this._scrollHeight) { this.Document.nativeHeight = Math.min(NumCast(this.Document.nativeHeight), this._scrollHeight); - this.layoutDoc.height = Math.min(NumCast(this.layoutDoc._height), (NumCast(this.layoutDoc._width) * NumCast(this.rootDoc.nativeHeight)) / NumCast(this.rootDoc.nativeWidth)); + this.layoutDoc.height = Math.min(NumCast(this.layoutDoc._height), (NumCast(this.layoutDoc._width) * NumCast(this.dataDoc.nativeHeight)) / NumCast(this.dataDoc.nativeWidth)); } }; const swidth = Math.max(NumCast(this.layoutDoc.nativeWidth), iframeContent.body.scrollWidth || 0); @@ -608,8 +608,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps }; back = (checkAvailable?: boolean) => { - const future = Cast(this.rootDoc[this.fieldKey + '_future'], listSpec('string')); - const history = Cast(this.rootDoc[this.fieldKey + '_history'], listSpec('string'), []); + const future = Cast(this.dataDoc[this.fieldKey + '_future'], listSpec('string')); + const history = Cast(this.dataDoc[this.fieldKey + '_history'], listSpec('string'), []); if (checkAvailable) return history.length; runInAction(() => { if (history.length) { @@ -677,7 +677,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps if (Field.toString(data) === this._url) return false; this._scrollHeight = 0; const oldUrl = this._url; - const history = Cast(this.rootDoc[this.fieldKey + '_history'], listSpec('string'), []); + const history = Cast(this.dataDoc[this.fieldKey + '_history'], listSpec('string'), []); const weburl = new WebField(Field.toString(data)); this.dataDoc[this.fieldKey + '_future'] = new List<string>([]); this.dataDoc[this.fieldKey + '_history'] = new List<string>([...(history || []), oldUrl]); @@ -1141,7 +1141,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps {...this.props} whenChildContentsActiveChanged={this.whenChildContentsActiveChanged} fieldKey={this.fieldKey + '_' + this._urlHash} - rootDoc={this.rootDoc} + Document={this.Document} layoutDoc={this.layoutDoc} dataDoc={this.dataDoc} setHeight={emptyFunction} diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index e8b9a98b7..95743a036 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -202,7 +202,6 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> { <DocumentView Document={this._finalLayout} addDocument={returnFalse} - rootSelected={returnFalse} //{this._textBox.props.isSelected} removeDocument={this.removeDoc} isDocumentActive={returnFalse} isContentActive={this.isContentActive} diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 130ac40c8..a914084f9 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -147,7 +147,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna createPivotForField = (e: React.MouseEvent) => { let container = this.props.tbox.props.DocumentView?.().props.docViewPath().lastElement(); if (container) { - const embedding = Doc.MakeEmbedding(container.rootDoc); + const embedding = Doc.MakeEmbedding(container.Document); embedding._type_collection = CollectionViewType.Time; const colHdrKey = '_' + container.LayoutFieldKey + '_columnHeaders'; let list = Cast(embedding[colHdrKey], listSpec(SchemaHeaderField)); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 0b63ac89d..42e8ace6e 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -15,7 +15,7 @@ import { EditorView } from 'prosemirror-view'; import { BsMarkdownFill } from 'react-icons/bs'; import { DateField } from '../../../../fields/DateField'; import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc'; -import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols'; +import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, DocData, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; @@ -110,6 +110,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps private _recordingStart: number = 0; private _ignoreScroll = false; private _lastText = ''; + private _hadDownFocus = false; private _focusSpeed: Opt<number>; private _keymap: any = undefined; private _rules: RichTextRules | undefined; @@ -117,6 +118,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps private _forceDownNode: Node | undefined; private _downX = 0; private _downY = 0; + private _downTime = 0; private _break = true; public ProseRef?: HTMLDivElement; public get EditorView() { @@ -142,13 +144,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps return (this.props.forceAutoHeight || this.layoutDoc._layout_autoHeight) && !this.props.ignoreAutoHeight; } @computed get textHeight() { - return NumCast(this.rootDoc[this.fieldKey + '_height']); + return NumCast(this.dataDoc[this.fieldKey + '_height']); } @computed get scrollHeight() { - return NumCast(this.rootDoc[this.fieldKey + '_scrollHeight']); + return NumCast(this.dataDoc[this.fieldKey + '_scrollHeight']); } @computed get sidebarHeight() { - return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + '_height']); + return !this.sidebarWidth() ? 0 : NumCast(this.dataDoc[this.SidebarKey + '_height']); } @computed get titleHeight() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0; @@ -164,7 +166,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } @computed get config() { this._keymap = buildKeymap(schema, this.props); - this._rules = new RichTextRules(this.rootDoc, this); + this._rules = new RichTextRules(this.Document, this); return { schema, plugins: [ @@ -188,7 +190,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps private gptRes: string = ''; public static PasteOnLoad: ClipboardEvent | undefined; - public static SelectOnLoad = ''; + private static SelectOnLoad: Doc | undefined; + public static SetSelectOnLoad(doc: Doc) { + FormattedTextBox.SelectOnLoad = doc; + } public static DontSelectInitialText = false; // whether initial text should be selected or not public static SelectOnLoadChar = ''; public static IsFragment(html: string) { @@ -308,15 +313,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps e.preventDefault(); e.stopPropagation(); const targetCreator = (annotationOn?: Doc) => { - const target = DocUtils.GetNewTextDoc('Note linked to ' + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn); - FormattedTextBox.SelectOnLoad = target[Id]; + const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn); + FormattedTextBox.SetSelectOnLoad(target); return target; }; DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docViewPath().lastElement(), () => this.getAnchor(true), targetCreator), e.pageX, e.pageY); }); const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to); - this.props.rootSelected() && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom); + this.props.rootSelected?.() && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom); let ele: Opt<HTMLDivElement> = undefined; try { const contents = window.getSelection()?.getRangeAt(0).cloneContents(); @@ -337,7 +342,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps const newText = state.doc.textBetween(0, state.doc.content.size, ' \n'); const newJson = JSON.stringify(state.toJSON()); const prevData = Cast(this.layoutDoc[this.fieldKey], RichTextField, null); // the actual text in the text box - const templateData = this.rootDoc !== this.layoutDoc ? prevData : undefined; // the default text stored in a layout template + const templateData = this.Document !== this.layoutDoc ? prevData : undefined; // the default text stored in a layout template const protoData = Cast(Cast(dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype const effectiveAcl = GetEffectiveAcl(dataDoc); @@ -363,7 +368,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps const numstring = NumCast(dataDoc[this.fieldKey], null); dataDoc[this.fieldKey] = numstring !== undefined ? Number(newText) : new RichTextField(newJson, newText); dataDoc[this.fieldKey + '_noTemplate'] = true; // mark the data field as being split from the template if it has been edited - textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: newText }); + textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.Document, text: newText }); unchanged = false; } } else { @@ -371,7 +376,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps dataDoc[this.fieldKey] = undefined; this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse((protoData || prevData).Data))); dataDoc[this.fieldKey + '_noTemplate'] = undefined; // mark the data field as not being split from any template it might have - ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: newText }); + ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, text: newText }); unchanged = false; } this._applyingChange = ''; @@ -388,7 +393,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps this._editorView.updateState(EditorState.fromJSON(this.config, json)); } } - if (window.getSelection()?.isCollapsed && this.props.rootSelected()) { + if (window.getSelection()?.isCollapsed && this.props.rootSelected?.()) { AnchorMenu.Instance.fadeOut(true); } } @@ -448,7 +453,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps tr = tr.setSelection(isNodeSel && false ? new NodeSelection(tr.doc.resolve(f)) : new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t))); this._editorView?.dispatch(tr); } - oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink); + oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.Document).forEach(LinkManager.Instance.deleteLink); }; updateTitle = () => { @@ -458,7 +463,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps (title.startsWith('-') || title.startsWith('@')) && this._editorView && !this.dataDoc.title_custom && - (Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey || this.fieldKey === 'text') + (Doc.LayoutFieldKey(this.Document) === this.fieldKey || this.fieldKey === 'text') ) { let node = this._editorView.state.doc; while (node.firstChild && node.firstChild.type.name !== 'text') node = node.firstChild; @@ -469,7 +474,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps if (!(cfield instanceof ComputedField)) { this.dataDoc.title = (prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? '...' : '')).trim(); if (str.startsWith('@') && str.length > 1) { - Doc.AddToMyPublished(this.rootDoc); + Doc.AddToMyPublished(this.Document); } } } @@ -478,7 +483,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps // creates links between terms in a document and published documents (myPublishedDocs) that have titles starting with an '@' hyperlinkTerm = (tr: any, target: Doc, newAutoLinks: Set<Doc>) => { const editorView = this._editorView; - if (editorView && (editorView as any).docView && !Doc.AreProtosEqual(target, this.rootDoc)) { + if (editorView && (editorView as any).docView && !Doc.AreProtosEqual(target, this.Document)) { const autoLinkTerm = StrCast(target.title).replace(/^@/, ''); var alink: Doc | undefined; this.findInNode(editorView, editorView.state.doc, autoLinkTerm).forEach(sel => { @@ -489,12 +494,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { alink = alink ?? - (LinkManager.Links(this.rootDoc).find( + (LinkManager.Links(this.Document).find( link => - Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.rootDoc) && // + Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.Document) && // Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target) ) || - DocUtils.MakeLink(this.rootDoc, target, { link_relationship: LinkManager.AutoKeywords })!); + DocUtils.MakeLink(this.Document, target, { link_relationship: LinkManager.AutoKeywords })!); newAutoLinks.add(alink); // DocCast(alink.link_anchor_1).followLinkLocation = 'add:right'; const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }]; @@ -600,7 +605,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } if (added) { draggedDoc._freeform_fitContentsToBox = true; - Doc.SetContainer(draggedDoc, this.rootDoc); + Doc.SetContainer(draggedDoc, this.Document); const view = this._editorView!; try { const pos = view.posAtCoords({ left: de.x, top: de.y })?.pos; @@ -821,7 +826,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps changeItems.push({ description: 'plain', event: undoBatch(() => { - Doc.setNativeView(this.rootDoc); + Doc.setNativeView(this.Document); this.layoutDoc.layout_autoHeightMargins = undefined; }), icon: 'eye', @@ -830,8 +835,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps description: 'metadata', event: undoBatch(() => { this.dataDoc.layout_meta = Cast(Doc.UserDoc().emptyHeader, Doc, null)?.layout; - this.rootDoc.layout_fieldKey = 'layout_meta'; - setTimeout(() => (this.rootDoc._headerHeight = this.rootDoc._layout_autoHeightMargins = 50), 50); + this.Document.layout_fieldKey = 'layout_meta'; + setTimeout(() => (this.layoutDoc._headerHeight = this.layoutDoc._layout_autoHeightMargins = 50), 50); }), icon: 'eye', }); @@ -842,8 +847,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps description: StrCast(note.title), event: undoBatch(() => { this.layoutDoc.layout_autoHeightMargins = undefined; - Doc.setNativeView(this.rootDoc); - DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note); + Doc.setNativeView(this.Document); + DocUtils.makeCustomViewClicked(this.Document, Docs.Create.TreeDocument, StrCast(note.title), note); }), icon: icon, }); @@ -882,40 +887,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps !Doc.noviceMode && appearanceItems.push({ description: 'Broadcast Message', - event: () => DocServer.GetRefField('rtfProto').then(proto => proto instanceof Doc && (proto.BROADCAST_MESSAGE = Cast(this.rootDoc[this.fieldKey], RichTextField)?.Text)), + event: () => DocServer.GetRefField('rtfProto').then(proto => proto instanceof Doc && (proto.BROADCAST_MESSAGE = Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text)), icon: 'expand-arrows-alt', }); appearanceItems.push({ description: 'Change Style...', noexpand: true, subitems: changeItems, icon: 'external-link-alt' }); - // this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" }); !Doc.noviceMode && appearanceItems.push({ description: 'Make Default Layout', event: () => { if (!this.layoutDoc.isTemplateDoc) { - const title = StrCast(this.rootDoc.title); - this.rootDoc.title = 'text'; - MakeTemplate(this.rootDoc, true, title); - } else if (!this.rootDoc.isTemplateDoc) { - const title = StrCast(this.rootDoc.title); - this.rootDoc.title = 'text'; - this.rootDoc.layout = this.layoutDoc.layout as string; - this.rootDoc.title = this.layoutDoc.isTemplateForField as string; - this.rootDoc.isTemplateDoc = false; - this.rootDoc.isTemplateForField = ''; - this.rootDoc.layout_fieldKey = 'layout'; - MakeTemplate(this.rootDoc, true, title); - setTimeout(() => { - this.rootDoc._layout_autoHeight = this.layoutDoc._layout_autoHeight; // layout_autoHeight, width and height - this.rootDoc._width = this.layoutDoc._width || 300; // are stored on the template, since we're getting rid of the old template - this.rootDoc._height = this.layoutDoc._height || 200; // we need to copy them over to the root. This should probably apply to all '_' fields - this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, 'string', null); - this.rootDoc.backgroundColor = Cast(this.layoutDoc.backgroundColor, 'string', null); - }, 10); + const title = StrCast(this.Document.title); + this.Document.title = 'text'; + MakeTemplate(this.Document, true, title); } - Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc); - Doc.AddDocToList(Cast(Doc.UserDoc().template_notes, Doc, null), 'data', this.rootDoc); + Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.Document); + Doc.AddDocToList(Cast(Doc.UserDoc().template_notes, Doc, null), 'data', this.Document); }, icon: 'eye', }); @@ -969,7 +957,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps generateImage = async () => { GPTPopup.Instance?.setTextAnchor(this.getAnchor(false)); - GPTPopup.Instance?.setImgTargetDoc(this.rootDoc); + GPTPopup.Instance?.setImgTargetDoc(this.Document); GPTPopup.Instance.addToCollection = this.props.addDocument; GPTPopup.Instance.setImgDesc((this.dataDoc.text as RichTextField)?.Text); GPTPopup.Instance.generateImage(); @@ -1069,13 +1057,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps anchor.presentation_zoomText = true; return anchor; } - return anchorDoc ?? this.rootDoc; + return anchorDoc ?? this.Document; } - return anchorDoc ?? this.rootDoc; + return anchorDoc ?? this.Document; } getView = async (doc: Doc) => { - if (DocListCast(this.rootDoc[this.SidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) { + if (DocListCast(this.dataDoc[this.SidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) { !this.SidebarShown && this.toggleSidebar(false); setTimeout(() => this._sidebarRef?.current?.makeDocUnfiltered(doc)); } @@ -1139,7 +1127,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps setTimeout(() => clearStyleSheetRules(FormattedTextBox._highlightStyleSheet), Math.max(this._focusSpeed || 0, 3000)); return focusSpeed; } else { - return this.props.focus(this.rootDoc, options); + return this.props.focus(this.Document, options); } } }; @@ -1148,12 +1136,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps // Since we also monitor all component height changes, this will update the document's height. resetNativeHeight = (scrollHeight: number) => { const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.layoutDoc._nativeHeight); - this.rootDoc[this.fieldKey + '_height'] = scrollHeight; + this.dataDoc[this.fieldKey + '_height'] = scrollHeight; if (nh) this.layoutDoc._nativeHeight = scrollHeight; }; @computed get contentScaling() { - return Doc.NativeAspect(this.rootDoc, this.dataDoc, false) ? this.props.NativeDimScaling?.() || 1 : 1; + return Doc.NativeAspect(this.Document, this.dataDoc, false) ? this.props.NativeDimScaling?.() || 1 : 1; } componentDidMount() { !this.props.dontSelectOnLoad && this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. @@ -1186,7 +1174,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps (!Array.from(FormattedTextBox._globalHighlights).includes('Bold Text') || this.props.isSelected()) && // layout_autoHeight && newHeight && - newHeight !== this.rootDoc.height && + newHeight !== this.layoutDoc.height && !this.props.dontRegisterView ) { this.props.setHeight?.(newHeight); @@ -1250,13 +1238,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps ); this._disposers.search = reaction( - () => Doc.IsSearchMatch(this.rootDoc), + () => Doc.IsSearchMatch(this.Document), search => (search ? this.highlightSearchTerms([Doc.SearchQuery()], search.searchMatch < 0) : this.unhighlightSearchTerms()), - { fireImmediately: Doc.IsSearchMatchUnmemoized(this.rootDoc) ? true : false } + { fireImmediately: Doc.IsSearchMatchUnmemoized(this.Document) ? true : false } ); this._disposers.selected = reaction( - () => this.props.rootSelected(), + () => this.props.rootSelected?.(), action(selected => { //selected && setTimeout(() => this.prepareForTyping()); if (FormattedTextBox._globalHighlights.has('Bold Text')) { @@ -1418,7 +1406,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps const dashField = view.state.schema.nodes.paragraph.create({}, [ view.state.schema.nodes.dashField.create({ fieldKey: 'text', docId: pdfAnchor[Id], hideKey: true, editable: false }, undefined, [ view.state.schema.marks.linkAnchor.create({ - allAnchors: [{ href: `/doc/${this.rootDoc[Id]}`, title: this.rootDoc.title, anchorId: `${this.rootDoc[Id]}` }], + allAnchors: [{ href: `/doc/${this.Document[Id]}`, title: this.Document.title, anchorId: `${this.Document[Id]}` }], title: `from: ${DocCast(pdfAnchor.embedContainer).title}`, noPreview: true, docref: false, @@ -1428,7 +1416,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps ]), ]); - const link = DocUtils.MakeLink(pdfAnchor, this.rootDoc, { link_relationship: 'PDF pasted' }); + const link = DocUtils.MakeLink(pdfAnchor, this.Document, { link_relationship: 'PDF pasted' }); if (link) { view.dispatch(view.state.tr.replaceSelectionWith(dashField, false).scrollIntoView().setMeta('paste', true).setMeta('uiEvent', 'paste')); } @@ -1451,7 +1439,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps const self = this; return new Plugin({ view(newView) { - runInAction(() => self.props.rootSelected() && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView)); + runInAction(() => self.props.rootSelected?.() && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView)); return new RichTextMenuPlugin({ editorProps: this.props }); }, }); @@ -1526,10 +1514,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps (this._editorView as any).TextView = this; } - const selectOnLoad = this.rootDoc[Id] === FormattedTextBox.SelectOnLoad && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())); + const selectOnLoad = Doc.AreProtosEqual(this.props.TemplateDataDocument ?? this.Document, FormattedTextBox.SelectOnLoad) && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())); if (this._editorView && selectOnLoad && !this.props.dontRegisterView && !this.props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) { const selLoadChar = FormattedTextBox.SelectOnLoadChar; - FormattedTextBox.SelectOnLoad = ''; + FormattedTextBox.SelectOnLoad = undefined; this.props.select(false); if (selLoadChar) { const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined; @@ -1569,8 +1557,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps ...(Doc.UserDoc().fontColor !== 'transparent' && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []), ...(Doc.UserDoc().fontStyle === 'italics' ? [schema.mark(schema.marks.em)] : []), ...(Doc.UserDoc().textDecoration === 'underline' ? [schema.mark(schema.marks.underline)] : []), - ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontFamily) })] : []), - ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontSize) })] : []), + ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontFamily) })] : []), + ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize) })] : []), ...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []), ...[schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })], ]; @@ -1623,8 +1611,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } this._downX = e.clientX; this._downY = e.clientY; + this._downTime = Date.now(); + this._hadDownFocus = this.ProseRef?.children[0].className.includes('focused') ?? false; FormattedTextBoxComment.textBox = this; - if (e.button === 0 && (this.props.rootSelected() || this.props.rootSelected()) && !e.altKey && !e.ctrlKey && !e.metaKey) { + if (e.button === 0 && this.props.rootSelected?.() && !e.altKey && !e.ctrlKey && !e.metaKey) { if (e.clientX < this.ProseRef!.getBoundingClientRect().right) { // stop propagation if not in sidebar, otherwise nested boxes will lose focus to outer boxes. e.stopPropagation(); // if the text box's content is active, then it consumes all down events @@ -1641,6 +1631,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps onPointerUp = (e: React.PointerEvent): void => { const editor = this._editorView!; const state = editor?.state; + if (!Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime) && !this._hadDownFocus) { + (this.ProseRef?.children[0] as HTMLElement)?.blur?.(); + } if (!state || !editor || !this.ProseRef?.children[0].className.includes('-focused')) return; if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu(); else if (this.props.isContentActive(true)) { @@ -1658,7 +1651,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps @action onDoubleClick = (e: React.MouseEvent): void => { FormattedTextBoxComment.textBox = this; - if (e.button === 0 && this.props.rootSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) { + if (e.button === 0 && this.props.rootSelected?.() && !e.altKey && !e.ctrlKey && !e.metaKey) { if (e.clientX < this.ProseRef!.getBoundingClientRect().right) { // stop propagation if not in sidebar e.stopPropagation(); // if the text box is selected, then it consumes all click events @@ -1669,7 +1662,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } FormattedTextBoxComment.Hide(); - if (e.buttons === 1 && this.props.rootSelected() && !e.altKey) { + if (e.buttons === 1 && this.props.rootSelected?.() && !e.altKey) { e.stopPropagation(); } }; @@ -1680,7 +1673,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps }; @action onFocused = (e: React.FocusEvent): void => { - console.log('FOCUSED = ' + this.layoutDoc.title + ' ' + this.props.rootSelected()); + console.log('FOCUSED = ' + this.layoutDoc.title + ' ' + this.props.rootSelected?.()); //applyDevTools.applyDevTools(this._editorView); this.ProseRef?.children[0] === e.nativeEvent.target && this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props, this.layoutDoc); e.stopPropagation(); @@ -1716,7 +1709,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps this._editorView!.dispatch(this._editorView!.state.tr.setSelection(NodeSelection.create(this._editorView!.state.doc, pcords.pos))); } } - if (this.props.rootSelected()) { + if (this.props.rootSelected?.()) { // if text box is selected, then it consumes all click events (e.nativeEvent as any).handledByInnerReactInstance = true; this.hitBulletTargets(e.clientX, e.clientY, !this._editorView?.state.selection.empty || this._forceUncollapse, false, this._forceDownNode, e.shiftKey); @@ -1730,7 +1723,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps clearStyleSheetRules(FormattedTextBox._bulletStyleSheet); const clickPos = this._editorView!.posAtCoords({ left: x, top: y }); let olistPos = clickPos?.pos; - if (clickPos && olistPos && this.props.rootSelected()) { + if (clickPos && olistPos && this.props.rootSelected?.()) { const clickNode = this._editorView?.state.doc.nodeAt(olistPos); const nodeBef = this._editorView?.state.doc.nodeAt(Math.max(0, olistPos - 1)); olistPos = nodeBef?.type === this._editorView?.state.schema.nodes.ordered_list ? olistPos - 1 : olistPos; @@ -1758,7 +1751,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } } startUndoTypingBatch() { - !this._undoTyping && (this._undoTyping = UndoManager.StartBatch('text edits on ' + this.rootDoc.title)); + !this._undoTyping && (this._undoTyping = UndoManager.StartBatch('text edits on ' + this.Document.title)); } public endUndoTypingBatch() { this._undoTyping?.end(); @@ -1767,7 +1760,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps @action onBlur = (e: any) => { - console.log('BLUREd = ' + this.layoutDoc.title + ' ' + this.props.rootSelected()); if (this.ProseRef?.children[0] !== e.nativeEvent.target) return; if (!(this.EditorView?.state.selection instanceof NodeSelection) || this.EditorView.state.selection.node.type !== this.EditorView.state.schema.nodes.footnote) { const stordMarks = this._editorView?.state.storedMarks?.slice(); @@ -1780,7 +1772,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps tr && this._editorView.dispatch(tr); } } - if (RichTextMenu.Instance?.view === this._editorView && !this.props.rootSelected()) { + if (RichTextMenu.Instance?.view === this._editorView && !this.props.rootSelected?.()) { RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined); } FormattedTextBox._hadSelection = window.getSelection()?.toString() !== ''; @@ -1808,12 +1800,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } this._lastText = curText; } - if (StrCast(this.rootDoc.title).startsWith('@') && !this.dataDoc.title_custom) { + if (StrCast(this.Document.title).startsWith('@') && !this.dataDoc.title_custom) { UndoManager.RunInBatch(() => { this.dataDoc.title_custom = true; this.dataDoc.layout_showTitle = 'title'; const tr = this._editorView!.state.tr; - this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(StrCast(this.rootDoc.title).length + 2))).deleteSelection()); + this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(StrCast(this.Document.title).length + 2))).deleteSelection()); }, 'titler'); } }; @@ -1839,7 +1831,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps let stopPropagation = true; for (var i = state.selection.from; i <= state.selection.to; i++) { const node = state.doc.resolve(i); - if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) && [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) { + if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) && [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.Document))) { e.preventDefault(); } } @@ -1862,7 +1854,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break; case ' ': if (e.code !== 'Space') { - [AclEdit, AclAugment, AclAdmin].includes(GetEffectiveAcl(this.rootDoc)) && + [AclEdit, AclAugment, AclAdmin].includes(GetEffectiveAcl(this.Document)) && this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }))); } break; @@ -1898,9 +1890,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.layout_maxAutoHeight, proseHeight), proseHeight); if (this.props.setHeight && scrollHeight && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation - const setScrollHeight = () => (this.rootDoc[this.fieldKey + '_scrollHeight'] = scrollHeight); + const setScrollHeight = () => (this.dataDoc[this.fieldKey + '_scrollHeight'] = scrollHeight); - if (this.rootDoc === this.layoutDoc || this.layoutDoc.resolvedDataDoc) { + if (this.Document === this.layoutDoc || this.layoutDoc.resolvedDataDoc) { setScrollHeight(); } else { setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... @@ -1916,7 +1908,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps }; sidebarMoveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.moveDocument(doc, targetCollection, addDocument, this.SidebarKey); sidebarRemDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, this.SidebarKey); - setSidebarHeight = (height: number) => (this.rootDoc[this.SidebarKey + '_height'] = height); + setSidebarHeight = (height: number) => (this.dataDoc[this.SidebarKey + '_height'] = height); sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth(); sidebarScreenToLocal = () => this.props @@ -1945,7 +1937,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps TraceMobx(); const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length; const color = !annotated ? Colors.WHITE : Colors.BLACK; - const backgroundColor = !annotated ? (this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK) : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor + (annotated ? ':annotated' : '')); + const backgroundColor = !annotated ? (this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK) : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.WidgetColor + (annotated ? ':annotated' : '')); return !annotated && (!this.props.isContentActive() || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? null : ( <div @@ -1967,7 +1959,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps <SidebarAnnos ref={this._sidebarRef} {...this.props} - rootDoc={this.rootDoc} + Document={this.Document} layoutDoc={this.layoutDoc} dataDoc={this.dataDoc} usePanelWidth={true} @@ -2022,12 +2014,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } cycleAlternateText = () => { if (this.layoutDoc._layout_enableAltContentUI) { - const usePath = this.rootDoc[`_${this.props.fieldKey}_usePath`]; - this.rootDoc[`_${this.props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined; + const usePath = this.layoutDoc[`_${this.props.fieldKey}_usePath`]; + this.layoutDoc[`_${this.props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined; } }; @computed get overlayAlternateIcon() { - const usePath = this.rootDoc[`_${this.props.fieldKey}_usePath`]; + const usePath = this.layoutDoc[`_${this.props.fieldKey}_usePath`]; return ( <Tooltip title={ @@ -2059,13 +2051,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps ); } @computed get fieldKey() { - const usePath = StrCast(this.rootDoc[`${this.props.fieldKey}_usePath`]); + const usePath = StrCast(this.layoutDoc[`${this.props.fieldKey}_usePath`]); return this.props.fieldKey + (usePath && (!usePath.includes(':hover') || this._isHovering || this.props.isContentActive()) ? `_${usePath.replace(':hover', '')}` : ''); } @observable _isHovering = false; onPassiveWheel = (e: WheelEvent) => { if (e.clientX > this.ProseRef!.getBoundingClientRect().right) { - if (this.rootDoc[this.SidebarKey + '_type_collection'] === CollectionViewType.Freeform) { + if (this.dataDoc[this.SidebarKey + '_type_collection'] === CollectionViewType.Freeform) { // if the scrolled freeform is a child of the sidebar component, we need to let the event go through // so react can let the freeform view handle it. We prevent default to stop any containing views from scrolling e.preventDefault(); @@ -2081,7 +2073,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps // prevent default if selected || child is active but this doc isn't scrollable if ( (this._scrollRef.current?.scrollHeight ?? 0) <= Math.ceil((height ? height : this.props.PanelHeight()) / scale) && // - (this.props.rootSelected() || this.isAnyChildContentActive()) + (this.props.rootSelected?.() || this.isAnyChildContentActive()) ) { e.preventDefault(); } diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index e7ca26d5c..4212d46eb 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -135,7 +135,7 @@ export class FormattedTextBoxComment { naft && LinkDocPreview.SetLinkInfo({ docProps: textBox.props, - linkSrc: textBox.rootDoc, + linkSrc: textBox.Document, linkDoc: linkDoc ? (DocServer.GetCachedRefField(linkDoc) as Doc) : undefined, location: (pos => [pos.left, pos.top + 25])(view.coordsAtPos(state.selection.from - Math.max(0, nbef - 1))), hrefs, diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index ec11079b4..08bad2d57 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -47,7 +47,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey } const canEdit = (state: any) => { - switch (GetEffectiveAcl(props.DataDoc)) { + switch (GetEffectiveAcl(props.TemplateDataDocument)) { case AclAugment: const prevNode = state.selection.$cursor.nodeBefore; const prevUser = !prevNode ? Doc.CurrentUserEmail : prevNode.marks[prevNode.marks.length - 1].attrs.userid; @@ -334,7 +334,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey //Command to create a blank space bind('Space', (state: EditorState, dispatch: (tx: Transaction) => void) => { - if (props.DataDoc && GetEffectiveAcl(props.DataDoc) != AclEdit && GetEffectiveAcl(props.DataDoc) != AclAugment && GetEffectiveAcl(props.DataDoc) != AclAdmin) return true; + if (props.TemplateDataDocument && GetEffectiveAcl(props.TemplateDataDocument) != AclEdit && GetEffectiveAcl(props.TemplateDataDocument) != AclAugment && GetEffectiveAcl(props.TemplateDataDocument) != AclAdmin) return true; const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); dispatch(splitMetadata(marks, state.tr)); return false; diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 7f9bfe753..791a1bfe7 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -186,7 +186,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { // finds font sizes and families in selection getActiveAlignment() { - if (this.view && this.TextView?.props.rootSelected()) { + if (this.view && this.TextView?.props.rootSelected?.()) { const path = (this.view.state.selection.$from as any).path; for (let i = path.length - 3; i < path.length && i >= 0; i -= 3) { if (path[i]?.type === this.view.state.schema.nodes.paragraph || path[i]?.type === this.view.state.schema.nodes.heading) { @@ -199,7 +199,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { // finds font sizes and families in selection getActiveListStyle() { - if (this.view && this.TextView?.props.rootSelected()) { + if (this.view && this.TextView?.props.rootSelected?.()) { const path = (this.view.state.selection.$from as any).path; for (let i = 0; i < path.length; i += 3) { if (path[i].type === this.view.state.schema.nodes.ordered_list) { @@ -219,7 +219,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { const activeSizes = new Set<string>(); const activeColors = new Set<string>(); const activeHighlights = new Set<string>(); - if (this.view && this.TextView?.props.rootSelected()) { + if (this.view && this.TextView?.props.rootSelected?.()) { const state = this.view.state; const pos = this.view.state.selection.$from; const marks: Mark[] = [...(state.storedMarks ?? [])]; @@ -239,7 +239,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { m.type === state.schema.marks.marker && activeHighlights.add(String(m.attrs.highlight)); }); } else if (SelectionManager.Views().some(dv => dv.ComponentView instanceof EquationBox)) { - SelectionManager.Views().forEach(dv => StrCast(dv.rootDoc._text_fontSize) && activeSizes.add(StrCast(dv.rootDoc._text_fontSize))); + SelectionManager.Views().forEach(dv => StrCast(dv.Document._text_fontSize) && activeSizes.add(StrCast(dv.Document._text_fontSize))); } return { activeFamilies: Array.from(activeFamilies), activeSizes: Array.from(activeSizes), activeColors: Array.from(activeColors), activeHighlights: Array.from(activeHighlights) }; } @@ -254,7 +254,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { //finds all active marks on selection in given group getActiveMarksOnSelection() { let activeMarks: MarkType[] = []; - if (!this.view || !this.TextView?.props.rootSelected()) return activeMarks; + if (!this.view || !this.TextView?.props.rootSelected?.()) return activeMarks; const markGroup = [schema.marks.noAutoLinkAnchor, schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript]; if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type); @@ -359,7 +359,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { this.view.focus(); } } else if (SelectionManager.Views().some(dv => dv.ComponentView instanceof EquationBox)) { - SelectionManager.Views().forEach(dv => (dv.rootDoc._text_fontSize = fontSize)); + SelectionManager.Views().forEach(dv => (dv.Document._text_fontSize = fontSize)); } else Doc.UserDoc().fontSize = fontSize; this.updateMenu(this.view, undefined, this.props, this.layoutDoc); }; @@ -447,7 +447,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { this.layoutDoc && (this.layoutDoc.layout_centered = !this.layoutDoc.layout_centered); }; align = (view: EditorView, dispatch: any, alignment: 'left' | 'right' | 'center') => { - if (this.TextView?.props.rootSelected()) { + if (this.TextView?.props.rootSelected?.()) { var tr = view.state.tr; view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos, parent, index) => { if ([schema.nodes.paragraph, schema.nodes.heading].includes(node.type)) { diff --git a/src/client/views/nodes/importBox/ImportElementBox.tsx b/src/client/views/nodes/importBox/ImportElementBox.tsx index 170626cd2..c4c89d8b1 100644 --- a/src/client/views/nodes/importBox/ImportElementBox.tsx +++ b/src/client/views/nodes/importBox/ImportElementBox.tsx @@ -24,7 +24,6 @@ export class ImportElementBox extends ViewBoxBaseComponent<FieldViewProps>() { LayoutTemplateString={undefined} Document={this.Document} isContentActive={returnFalse} - DataDoc={undefined} addDocument={returnFalse} ScreenToLocalTransform={this.screenToLocalXf} hideResizeHandles={true} diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 71c585c38..2303e4126 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -1067,7 +1067,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { setTimeout(() => this.removeDocument(doc), 0); return false; } - } else { + } else if (doc.type !== DocumentType.PRES) { if (!doc.presentation_targetDoc) doc.title = doc.title + ' - Slide'; doc.presentation_targetDoc = doc.createdFrom; // dropped document will be a new embedding of an embedded document somewhere else. doc.presentation_movement = PresMovement.Zoom; @@ -2338,9 +2338,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { const mode = StrCast(this.Document._type_collection) as CollectionViewType; const isMini: boolean = this.toolbarWidth <= 100; return ( - <div - className={`presBox-buttons${Doc.IsInMyOverlay(this.Document) ? ' inOverlay' : ''}`} - style={{ background: Doc.ActivePresentation === this.Document ? Colors.LIGHT_BLUE : undefined, display: !this.Document._chromeHidden ? 'none' : undefined }}> + <div className={`presBox-buttons${Doc.IsInMyOverlay(this.Document) ? ' inOverlay' : ''}`} style={{ background: Doc.ActivePresentation === this.Document ? Colors.LIGHT_BLUE : undefined }}> {isMini ? null : ( <select className="presBox-viewPicker" style={{ display: this.layoutDoc.presentation_status === 'edit' ? 'block' : 'none' }} onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}> <option onPointerDown={StopEvent} value={CollectionViewType.Stacking}> diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 6a8edee0d..f8aebd021 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -56,7 +56,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { // Since this node is being rendered with a template, this method retrieves // the actual slide being rendered from the auto-generated rendering template @computed get slideDoc() { - return Cast(this.Document.rootDocument, Doc, null) || this.props.Document; + return this.props.TemplateDataDocument ?? this.props.Document; } // this is the document in the workspaces that is targeted by the slide @@ -104,7 +104,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { <div className="presItem-embedded" style={{ height: this.embedHeight(), width: '50%' }}> <DocumentView Document={PresBox.targetRenderedDoc(this.slideDoc)} - DataDoc={undefined} //this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]} PanelWidth={this.embedWidth} PanelHeight={this.embedHeight} isContentActive={this.props.isContentActive} @@ -116,7 +115,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { childFilters={this.props.childFilters} childFiltersByRanges={this.props.childFiltersByRanges} searchFilterDocs={this.props.searchFilterDocs} - rootSelected={returnFalse} addDocument={returnFalse} removeDocument={returnFalse} fitContentsToBox={returnTrue} diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index f1686a6ea..7f71ee678 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -100,12 +100,10 @@ export class TopBar extends React.Component { <div className="collectionMenu-contMenuButtons" style={{ height: '100%' }}> <CollectionLinearView Document={selDoc} - DataDoc={undefined} fieldKey="data" dropAction="embed" setHeight={returnFalse} styleProvider={DefaultStyleProvider} - rootSelected={returnFalse} bringToFront={emptyFunction} select={emptyFunction} isContentActive={returnTrue} diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 51dacd181..525ff7ec0 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -527,14 +527,14 @@ export namespace Doc { } export function MakeEmbedding(doc: Doc, id?: string) { - const embedding = !GetT(doc, 'isDataDoc', 'boolean', true) && doc.proto ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id); + const embedding = (!GetT(doc, 'isDataDoc', 'boolean', true) && doc.proto) || doc.type === DocumentType.CONFIG ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id); const layout = Doc.LayoutField(embedding); if (layout instanceof Doc && layout !== embedding && layout === Doc.Layout(embedding)) { Doc.SetLayout(embedding, Doc.MakeEmbedding(layout)); } embedding.createdFrom = doc; embedding.proto_embeddingId = Doc.GetProto(doc).proto_embeddingId = DocListCast(Doc.GetProto(doc).proto_embeddings).length - 1; - embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`); + !Doc.GetT(embedding, 'title', 'string', true) && (embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`)); embedding.author = Doc.CurrentUserEmail; Doc.AddDocToList(doc[DocData], 'proto_embeddings', embedding); @@ -751,7 +751,7 @@ export namespace Doc { }); } - const _pendingMap: Map<string, boolean> = new Map(); + const _pendingMap = new Set<string>(); // // Returns an expanded template layout for a target data document if there is a template relationship // between the two. If so, the layoutDoc is expanded into a new document that inherits the properties @@ -773,19 +773,19 @@ export namespace Doc { if (templateLayoutDoc.resolvedDataDoc instanceof Promise) { expandedTemplateLayout = undefined; - _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey, true); - } else if (expandedTemplateLayout === undefined && !_pendingMap.get(targetDoc[Id] + expandedLayoutFieldKey)) { - if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc))) { + _pendingMap.add(targetDoc[Id] + expandedLayoutFieldKey); + } else if (expandedTemplateLayout === undefined && !_pendingMap.has(targetDoc[Id] + expandedLayoutFieldKey)) { + if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument ?? Doc.GetProto(targetDoc))) { expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params } else { templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = DocCast(templateLayoutDoc.proto, templateLayoutDoc)); // if the template has already been applied (ie, a nested template), then use the template's prototype if (!targetDoc[expandedLayoutFieldKey]) { - _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey, true); + _pendingMap.add(targetDoc[Id] + expandedLayoutFieldKey); setTimeout( action(() => { const newLayoutDoc = Doc.MakeDelegate(templateLayoutDoc, undefined, '[' + templateLayoutDoc.title + ']'); - newLayoutDoc.rootDocument = targetDoc; const dataDoc = Doc.GetProto(targetDoc); + newLayoutDoc.rootDocument = targetDoc; newLayoutDoc.embedContainer = targetDoc; newLayoutDoc.resolvedDataDoc = dataDoc; newLayoutDoc['acl-Guest'] = SharingPermissions.Edit; diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 97f771c16..bdd657575 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -384,11 +384,9 @@ export class MobileInterface extends React.Component { <div style={{ position: 'relative', top: '198px', height: `calc(100% - 350px)`, width: '100%', left: '0%' }}> <DocumentView Document={this.mainContainer} - DataDoc={undefined} addDocument={returnFalse} addDocTab={returnFalse} pinToPres={emptyFunction} - rootSelected={returnFalse} removeDocument={undefined} ScreenToLocalTransform={Transform.Identity} PanelWidth={this.returnWidth} diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index ea5d8cb33..391f67bbb 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -1,7 +1,7 @@ import * as formidable from 'formidable'; import { createReadStream, createWriteStream, unlink, writeFile } from 'fs'; -import { basename, dirname, extname, normalize } from 'path'; -import * as sharp from 'sharp'; +import * as path from 'path'; +import Jimp from 'jimp'; import { filesDirectory, publicDirectory } from '..'; import { retrocycle } from '../../decycler/decycler'; import { DashUploadUtils, InjectSize, SizeSuffix } from '../DashUploadUtils'; @@ -29,11 +29,11 @@ export enum Directory { } export function serverPathToFile(directory: Directory, filename: string) { - return normalize(`${filesDirectory}/${directory}/${filename}`); + return path.normalize(`${filesDirectory}/${directory}/${filename}`); } export function pathToDirectory(directory: Directory) { - return normalize(`${filesDirectory}/${directory}`); + return path.normalize(`${filesDirectory}/${directory}`); } export function clientPathToFile(directory: Directory, filename: string) { @@ -74,6 +74,8 @@ export default class UploadManager extends ApiManager { filesize = value; } }); + fileguids.split(';').map(guid => DashUploadUtils.uploadProgress.set(guid, `upload starting`)); + form.on('progress', e => fileguids.split(';').map(guid => DashUploadUtils.uploadProgress.set(guid, `read:(${Math.round((100 * +e) / +filesize)}%) ${e} of ${filesize}`))); form.keepExtensions = true; form.uploadDir = pathToDirectory(Directory.parsed_files); @@ -92,6 +94,8 @@ export default class UploadManager extends ApiManager { result: { name: 'failed upload', message: `${_err.message}` }, }); } + fileguids.split(';').map(guid => DashUploadUtils.uploadProgress.set(guid, `resampling images`)); + for (const key in files) { const f = files[key]; if (!Array.isArray(f)) { @@ -164,7 +168,7 @@ export default class UploadManager extends ApiManager { if (error) { return res.send(); } - await DashUploadUtils.outputResizedImages(() => createReadStream(resolvedPath), resolvedName, pathToDirectory(Directory.images)); + await DashUploadUtils.outputResizedImages(resolvedPath, resolvedName, pathToDirectory(Directory.images)); res.send({ accessPaths: { agnostic: DashUploadUtils.getAccessPaths(Directory.images, resolvedName), @@ -255,42 +259,20 @@ export default class UploadManager extends ApiManager { if (!entryName.startsWith('files/')) { return; } - const extension = extname(entryName); + const extension = path.extname(entryName); const pathname = publicDirectory + '/' + entry.entryName; const targetname = publicDirectory + '/' + entryName; try { zip.extractEntryTo(entry.entryName, publicDirectory, true, false); createReadStream(pathname).pipe(createWriteStream(targetname)); - if (extension !== '.pdf') { - const { pngs, jpgs } = AcceptableMedia; - const resizers = [ - { resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Small }, - { resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Medium }, - { resizer: sharp().resize(900, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Large }, - ]; - let isImage = false; - if (pngs.includes(extension)) { - resizers.forEach(element => { - element.resizer = element.resizer.png(); - }); - isImage = true; - } else if (jpgs.includes(extension)) { - resizers.forEach(element => { - element.resizer = element.resizer.jpeg(); - }); - isImage = true; - } - if (isImage) { - resizers.forEach(resizer => { - createReadStream(pathname) - .on('error', e => console.log('Resizing read:' + e)) - .pipe(resizer.resizer) - .on('error', e => console.log('Resizing write: ' + e)) - .pipe(createWriteStream(targetname.replace('_o' + extension, resizer.suffix + extension)).on('error', e => console.log('Resizing write: ' + e))); - }); - } - } - unlink(pathname, () => {}); + Jimp.read(pathname).then(img => { + DashUploadUtils.imageResampleSizes(extension).forEach(({ width, suffix }) => { + const outputPath = InjectSize(targetname, suffix); + if (!width) createReadStream(pathname).pipe(createWriteStream(outputPath)); + else img = img.resize(width, Jimp.AUTO).write(outputPath); + }); + unlink(pathname, () => {}); + }); } catch (e) { console.log(e); } @@ -346,7 +328,7 @@ export default class UploadManager extends ApiManager { method: Method.POST, subscription: '/uploadURI', secureHandler: ({ req, res }) => { - const uri = req.body.uri; + const uri: any = req.body.uri; const filename = req.body.name; const origSuffix = req.body.nosuffix ? SizeSuffix.None : SizeSuffix.Original; const deleteFiles = req.body.replaceRootFilename; @@ -362,36 +344,16 @@ export default class UploadManager extends ApiManager { .map((f: any) => fs.unlinkSync(path + f)); } return imageDataUri.outputFile(uri, serverPathToFile(Directory.images, InjectSize(filename, origSuffix))).then((savedName: string) => { - const ext = extname(savedName).toLowerCase(); - const { pngs, jpgs } = AcceptableMedia; - const resizers = !origSuffix - ? [{ resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Medium }] - : [ - { resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Small }, - { resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Medium }, - { resizer: sharp().resize(900, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Large }, - ]; - let isImage = false; - if (pngs.includes(ext)) { - resizers.forEach(element => { - element.resizer = element.resizer.png(); - }); - isImage = true; - } else if (jpgs.includes(ext)) { - resizers.forEach(element => { - element.resizer = element.resizer.jpeg(); - }); - isImage = true; - } - if (isImage) { - resizers.forEach(resizer => { - const path = serverPathToFile(Directory.images, InjectSize(filename, resizer.suffix) + ext); - createReadStream(savedName) - .on('error', e => console.log('Resizing read:' + e)) - .pipe(resizer.resizer) - .on('error', e => console.log('Resizing write: ' + e)) - .pipe(createWriteStream(path).on('error', e => console.log('Resizing write: ' + e))); - }); + const ext = path.extname(savedName).toLowerCase(); + if (AcceptableMedia.imageFormats.includes(ext)) { + Jimp.read(savedName).then(img => + (!origSuffix ? [{ width: 400, suffix: SizeSuffix.Medium }] : Object.values(DashUploadUtils.Sizes)) // + .forEach(({ width, suffix }) => { + const outputPath = InjectSize(filename, suffix); + if (!width) createReadStream(savedName).pipe(createWriteStream(outputPath)); + else img = img.resize(width, Jimp.AUTO).write(outputPath); + }) + ); } res.send(clientPathToFile(Directory.images, filename + ext)); }); diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index 19cb3f240..2053ae448 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -5,8 +5,7 @@ import { File } from 'formidable'; import { createReadStream, createWriteStream, existsSync, readFileSync, rename, unlinkSync, writeFile } from 'fs'; import * as path from 'path'; import { basename } from 'path'; -import * as sharp from 'sharp'; -import { Readable, Stream } from 'stream'; +import Jimp from 'jimp'; import { filesDirectory, publicDirectory } from '.'; import { Opt } from '../fields/Doc'; import { ParsedPDF } from '../server/PdfTypes'; @@ -55,9 +54,9 @@ export namespace DashUploadUtils { } export const Sizes: { [size: string]: Size } = { - SMALL: { width: 100, suffix: SizeSuffix.Small }, + LARGE: { width: 800, suffix: SizeSuffix.Large }, MEDIUM: { width: 400, suffix: SizeSuffix.Medium }, - LARGE: { width: 900, suffix: SizeSuffix.Large }, + SMALL: { width: 100, suffix: SizeSuffix.Small }, }; export function validateExtension(url: string) { @@ -197,7 +196,7 @@ export namespace DashUploadUtils { const isAzureOn = usingAzure(); const { type, path, name } = file; const types = type?.split('/') ?? []; - uploadProgress.set(overwriteGuid ?? name, 'uploading'); // If the client sent a guid it uses to track upload progress, use that guid. Otherwise, use the file's name. + // uploadProgress.set(overwriteGuid ?? name, 'uploading'); // If the client sent a guid it uses to track upload progress, use that guid. Otherwise, use the file's name. const category = types[0]; let format = `.${types[1]}`; @@ -367,7 +366,7 @@ export namespace DashUploadUtils { } export interface ImageResizer { - resizer?: sharp.Sharp; + width?: any; // sharp.Sharp; suffix: SizeSuffix; } @@ -540,7 +539,7 @@ export namespace DashUploadUtils { writtenFiles = {}; } } else { - writtenFiles = await outputResizedImages(() => request(requestable), resolved, pathToDirectory(Directory.images)); + writtenFiles = await outputResizedImages(metadata.source, resolved, pathToDirectory(Directory.images)); } for (const suffix of Object.keys(writtenFiles)) { information.accessPaths[suffix] = getAccessPaths(images, writtenFiles[suffix]); @@ -595,57 +594,39 @@ export namespace DashUploadUtils { * @param outputDirectory the directory to output to, usually Directory.Images * @returns a map with suffixes as keys and resized filenames as values. */ - export async function outputResizedImages(streamProvider: () => Stream | Promise<Stream>, outputFileName: string, outputDirectory: string) { + export async function outputResizedImages(sourcePath: string, outputFileName: string, outputDirectory: string) { const writtenFiles: { [suffix: string]: string } = {}; - for (const { resizer, suffix } of resizers(path.extname(outputFileName))) { - const outputPath = path.resolve(outputDirectory, (writtenFiles[suffix] = InjectSize(outputFileName, suffix))); - await new Promise<void>(async (resolve, reject) => { - const source = streamProvider(); - let readStream = source instanceof Promise ? await source : source; - let error = false; - if (resizer) { - readStream = readStream.pipe(resizer.withMetadata()).on('error', async args => { - error = true; - if (error) { - const source2 = streamProvider(); - let readStream2: Stream | undefined; - readStream2 = source2 instanceof Promise ? await source2 : source2; - readStream2?.pipe(createWriteStream(outputPath)).on('error', resolve).on('close', resolve); - } - }); - } - !error && readStream?.pipe(createWriteStream(outputPath)).on('error', resolve).on('close', resolve); + const sizes = imageResampleSizes(path.extname(outputFileName)); + const outputPath = (suffix: SizeSuffix) => path.resolve(outputDirectory, (writtenFiles[suffix] = InjectSize(outputFileName, suffix))); + await Promise.all( + sizes.filter(({ width }) => !width).map(({ suffix }) => + new Promise<void>(res => createReadStream(sourcePath).pipe(createWriteStream(outputPath(suffix))).on('close', res)) + )); // prettier-ignore + return Jimp.read(sourcePath) + .then(async img => { + await Promise.all( sizes.filter(({ width }) => width) .map(({ width, suffix }) => + img = img.resize(width, Jimp.AUTO).write(outputPath(suffix)) + )); // prettier-ignore + return writtenFiles; + }) + .catch(e => { + console.log('ERROR' + e); + return writtenFiles; }); - } - return writtenFiles; } /** * define the resizers to use * @param ext the extension - * @returns an array of resizer functions from sharp + * @returns an array of resize descriptions */ - function resizers(ext: string): DashUploadUtils.ImageResizer[] { + export function imageResampleSizes(ext: string): DashUploadUtils.ImageResizer[] { return [ { suffix: SizeSuffix.Original }, - ...Object.values(DashUploadUtils.Sizes).map(({ suffix, width }) => { - let initial: sharp.Sharp | undefined = sharp({ failOnError: false }).resize(width, undefined, { withoutEnlargement: true }); - if (pngs.includes(ext)) { - initial = initial.png(pngOptions); - } else if (jpgs.includes(ext)) { - initial = initial.jpeg(); - } else if (webps.includes(ext)) { - initial = initial.webp(); - } else if (tiffs.includes(ext)) { - initial = initial.tiff(); - } else if (ext === '.gif') { - initial = undefined; - } - return { - resizer: suffix === '_o' ? undefined : initial, - suffix, - }; - }), + ...[...(AcceptableMedia.imageFormats.includes(ext.toLowerCase()) ? Object.values(DashUploadUtils.Sizes) : [])].map(({ suffix, width }) => ({ + width, + suffix, + })), ]; } } diff --git a/src/server/downsize.ts b/src/server/downsize.ts deleted file mode 100644 index 382994e2d..000000000 --- a/src/server/downsize.ts +++ /dev/null @@ -1,40 +0,0 @@ -import * as fs from 'fs'; -import * as sharp from 'sharp'; - -const folder = "./src/server/public/files/"; -const pngTypes = ["png", "PNG"]; -const jpgTypes = ["jpg", "JPG", "jpeg", "JPEG"]; -const smallResizer = sharp().resize(100); -fs.readdir(folder, async (err, files) => { - if (err) { - console.log("readdir:" + err); - return; - } - // files.forEach(file => { - // if (file.includes("_s") || file.includes("_m") || file.includes("_l")) { - // fs.unlink(folder + file, () => { }); - // } - // }); - for (const file of files) { - const filesplit = file.split("."); - const resizers = [ - { resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: "_s" }, - { resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: "_m" }, - { resizer: sharp().resize(900, undefined, { withoutEnlargement: true }), suffix: "_l" }, - ]; - if (pngTypes.some(type => file.endsWith(type))) { - resizers.forEach(element => { - element.resizer = element.resizer.png(); - }); - } else if (jpgTypes.some(type => file.endsWith(type))) { - resizers.forEach(element => { - element.resizer = element.resizer.jpeg(); - }); - } else { - continue; - } - resizers.forEach(resizer => { - fs.createReadStream(folder + file).pipe(resizer.resizer).pipe(fs.createWriteStream(folder + filesplit[0] + resizer.suffix + "." + filesplit[1])); - }); - } -});
\ No newline at end of file |