aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2025-04-14 18:35:49 -0400
committerbobzel <zzzman@gmail.com>2025-04-14 18:35:49 -0400
commitd818ef151ca65008e5c6bb5e92b709decb3026d8 (patch)
treeae1d821c717cfb4b38c36b519d03b45ed90e9831 /src
parent1525fe600142d955fa24e939322f45cbca9d1cba (diff)
fixed how templates are expanded to avoid template sub-component conflicts by changing how field keys are named. fixed various Cast functions to be more typesafe by including undefined as part of return type. overhaul of Doc.MakeClone, MakeCopy, FindRefernces - makeClone is no longer async. fixed inlined docs in text docs.
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/DocUtils.ts41
-rw-r--r--src/client/documents/DocumentTypes.ts2
-rw-r--r--src/client/documents/Documents.ts8
-rw-r--r--src/client/util/CurrentUserUtils.ts37
-rw-r--r--src/client/util/DictationManager.ts2
-rw-r--r--src/client/util/DocumentManager.ts2
-rw-r--r--src/client/util/DragManager.ts40
-rw-r--r--src/client/util/DropConverter.ts6
-rw-r--r--src/client/util/Import & Export/ImageUtils.ts2
-rw-r--r--src/client/util/LinkFollower.ts32
-rw-r--r--src/client/util/LinkManager.ts43
-rw-r--r--src/client/util/SearchUtil.ts6
-rw-r--r--src/client/util/SettingsManager.tsx2
-rw-r--r--src/client/views/DocComponent.tsx8
-rw-r--r--src/client/views/DocumentButtonBar.tsx8
-rw-r--r--src/client/views/FilterPanel.tsx4
-rw-r--r--src/client/views/GlobalKeyHandler.ts6
-rw-r--r--src/client/views/InkStrokeProperties.ts2
-rw-r--r--src/client/views/InkTranscription.tsx6
-rw-r--r--src/client/views/LightboxView.tsx4
-rw-r--r--src/client/views/Main.tsx4
-rw-r--r--src/client/views/MainView.tsx42
-rw-r--r--src/client/views/PinFuncs.ts8
-rw-r--r--src/client/views/PropertiesButtons.tsx2
-rw-r--r--src/client/views/PropertiesView.tsx8
-rw-r--r--src/client/views/StyleProvider.tsx12
-rw-r--r--src/client/views/StyleProviderQuiz.tsx2
-rw-r--r--src/client/views/TagsView.tsx6
-rw-r--r--src/client/views/animationtimeline/Timeline.tsx2
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx3
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx16
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.tsx11
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx12
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx4
-rw-r--r--src/client/views/collections/CollectionSubView.tsx37
-rw-r--r--src/client/views/collections/TabDocView.tsx14
-rw-r--r--src/client/views/collections/TreeView.tsx20
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx31
-rw-r--r--src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx6
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx4
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx4
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx2
-rw-r--r--src/client/views/global/globalScripts.ts37
-rw-r--r--src/client/views/newlightbox/NewLightboxView.tsx2
-rw-r--r--src/client/views/nodes/AudioBox.tsx2
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx4
-rw-r--r--src/client/views/nodes/DataVizBox/DataVizBox.tsx2
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx4
-rw-r--r--src/client/views/nodes/DocumentView.tsx100
-rw-r--r--src/client/views/nodes/FieldView.tsx2
-rw-r--r--src/client/views/nodes/IconTagBox.tsx2
-rw-r--r--src/client/views/nodes/ImageBox.tsx24
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx8
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx4
-rw-r--r--src/client/views/nodes/PDFBox.tsx2
-rw-r--r--src/client/views/nodes/RecordingBox/RecordingBox.tsx2
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx2
-rw-r--r--src/client/views/nodes/ScriptingBox.tsx13
-rw-r--r--src/client/views/nodes/WebBox.tsx18
-rw-r--r--src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx2
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx55
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx6
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts19
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx187
-rw-r--r--src/client/views/nodes/trails/PresSlideBox.scss (renamed from src/client/views/nodes/trails/PresElementBox.scss)0
-rw-r--r--src/client/views/nodes/trails/PresSlideBox.tsx (renamed from src/client/views/nodes/trails/PresElementBox.tsx)34
-rw-r--r--src/client/views/nodes/trails/index.ts2
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.tsx2
-rw-r--r--src/client/views/search/FaceRecognitionHandler.tsx4
-rw-r--r--src/client/views/search/SearchBox.tsx2
-rw-r--r--src/client/views/smartdraw/StickerPalette.tsx13
-rw-r--r--src/debug/Repl.tsx6
-rw-r--r--src/fields/Doc.ts486
-rw-r--r--src/fields/List.ts6
-rw-r--r--src/fields/Proxy.ts64
-rw-r--r--src/fields/Schema.ts21
-rw-r--r--src/fields/ScriptField.ts12
-rw-r--r--src/fields/Types.ts91
-rw-r--r--src/fields/util.ts10
81 files changed, 880 insertions, 887 deletions
diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index 5a8230847..7845c80aa 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -55,13 +55,13 @@ export namespace DocUtils {
const matchLink = (val: string, anchor: Doc) => {
const linkedToExp = (val ?? '').split('=');
if (linkedToExp.length === 1) return Field.toScriptString(anchor) === val;
- return Field.toScriptString(DocCast(anchor[linkedToExp[0]])) === linkedToExp[1];
+ return DocCast(anchor[linkedToExp[0]]) && Field.toScriptString(DocCast(anchor[linkedToExp[0]])!) === linkedToExp[1];
};
// prettier-ignore
return (value === Doc.FilterNone && !allLinks.length) ||
(value === Doc.FilterAny && !!allLinks.length) ||
- (allLinks.some(link => matchLink(value as string, DocCast(link.link_anchor_1)) ||
- matchLink(value as string, DocCast(link.link_anchor_2)) ));
+ (allLinks.some(link => (DocCast(link.link_anchor_1) && matchLink(value as string, DocCast(link.link_anchor_1)!)) ||
+ (DocCast(link.link_anchor_2) && matchLink(value as string, DocCast(link.link_anchor_2)!)) ));
}
if (typeof value === 'string') {
value = value.replace(`,${ClientUtils.noRecursionHack}`, '');
@@ -266,7 +266,7 @@ export namespace DocUtils {
Object.keys(funcs)
.filter(key => !key.endsWith('-setter'))
.forEach(key => {
- const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+ const cfield = ComputedField.DisableCompute(() => FieldValue(doc[key]));
const func = funcs[key];
if (ScriptCast(cfield)?.script.originalScript !== func) {
const setFunc = Cast(funcs[key + '-setter'], 'string', null);
@@ -375,12 +375,13 @@ export namespace DocUtils {
const documentList: ContextMenuProps[] = foo
.filter(btnDoc => !btnDoc.hidden)
- .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null))
+ .map(btnDoc => DocCast(btnDoc?.dragFactory))
.filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc.title)
+ .map(doc => doc!)
.map(dragDoc => ({
description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''),
event: undoable(() => {
- const newDoc = (Doc.isTemplateDoc(dragDoc) ? DocUtils.delegateDragFactory : DocUtils.copyDragFactory)(dragDoc);
+ const newDoc = (dragDoc.isTemplateDoc ? DocUtils.delegateDragFactory : DocUtils.copyDragFactory)(dragDoc);
if (newDoc) {
newDoc._author = ClientUtils.CurrentUserEmail();
newDoc.x = x;
@@ -437,6 +438,7 @@ export namespace DocUtils {
.filter(btnDoc => !btnDoc.hidden)
.map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null))
.filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc !== Doc.UserDoc().emptyNote && doc.title)
+ .map(doc => doc!)
.map(dragDoc => ({
description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''),
event: undoable(() => {
@@ -494,11 +496,11 @@ export namespace DocUtils {
.map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc)
.filter(d => d.isTemplateDoc);
// bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document. but type is not reliable and there could be other types of template searches so this should be generalized
- // first try to find a template that matches the specific document type (<typeName>_<templateName>). otherwise, fallback to a general match on <templateName>
+ // first try to find a template that matches the specific document type (<typeName><TemplateName>). otherwise, fallback to a general match on <templateName>
!docLayoutTemplate &&
allTemplates.forEach(tempDoc => {
const templateType = StrCast(doc[templateName + '_fieldKey'] || doc.type);
- StrCast(tempDoc.title) === templateName + '_' + templateType && (docLayoutTemplate = tempDoc);
+ StrCast(tempDoc.title) === templateName + (templateType[0].toUpperCase() + templateType.slice(1)) && (docLayoutTemplate = tempDoc);
});
!docLayoutTemplate &&
allTemplates.forEach(tempDoc => {
@@ -522,18 +524,13 @@ export namespace DocUtils {
Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined);
}
} else {
- let fieldTemplate: Opt<Doc>;
- if (doc.data instanceof RichTextField || typeof doc.data === 'string') {
- fieldTemplate = Docs.Create.TextDocument('', options);
- } else if (doc.data instanceof PdfField) {
- fieldTemplate = Docs.Create.PdfDocument('http://www.msn.com', options);
- } else if (doc.data instanceof VideoField) {
- fieldTemplate = Docs.Create.VideoDocument('http://www.cs.brown.edu', options);
- } else if (doc.data instanceof AudioField) {
- fieldTemplate = Docs.Create.AudioDocument('http://www.cs.brown.edu', options);
- } else if (doc.data instanceof ImageField) {
- fieldTemplate = Docs.Create.ImageDocument('http://www.cs.brown.edu', options);
- }
+ const fieldTemplate = (() => {
+ if (doc.data instanceof RichTextField || typeof doc.data === 'string') return Docs.Create.TextDocument('', options);
+ if (doc.data instanceof PdfField) return Docs.Create.PdfDocument('http://www.msn.com', options);
+ if (doc.data instanceof VideoField) return Docs.Create.VideoDocument('http://www.cs.brown.edu', options);
+ if (doc.data instanceof AudioField) return Docs.Create.AudioDocument('http://www.cs.brown.edu', options);
+ if (doc.data instanceof ImageField) return Docs.Create.ImageDocument('http://www.cs.brown.edu', options);
+ })();
const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + '(' + doc.title + ')', isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) });
fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate);
docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined);
@@ -904,8 +901,8 @@ export namespace DocUtils {
return ndoc;
}
- export async function Zip(doc: Doc, zipFilename = 'dashExport.zip') {
- const { clone, map, linkMap } = await Doc.MakeClone(doc);
+ export function Zip(doc: Doc, zipFilename = 'dashExport.zip') {
+ const { clone, map, linkMap } = Doc.MakeClone(doc);
const proms = new Set<string>();
function replacer(key: string, value: { url: string; [key: string]: unknown }) {
if (key && ['branchOf', 'cloneOf', 'cursors'].includes(key)) return undefined;
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index dd0985182..5c6559836 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -36,7 +36,7 @@ export enum DocumentType {
// special purpose wrappers that either take no data or are compositions of lower level types
LINK = 'link',
PRES = 'presentation',
- PRESELEMENT = 'preselement',
+ PRESSLIDE = 'presslide',
COMPARISON = 'comparison',
PUSHPIN = 'pushpin',
MAPROUTE = 'maproute',
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d93a432d4..bf9cc5bd4 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -217,7 +217,7 @@ export class DocumentOptions {
author?: string; // STRt = new StrInfo('creator of document'); // bcz: don't change this. Otherwise, the userDoc's field Infos will have a FieldInfo assigned to its author field which will render it unreadable
author_date?: DATEt = new DateInfo('date the document was created', true);
annotationOn?: DOCt = new DocInfo('document annotated by this document', false);
- rootDocument?: DOCt = new DocInfo('document that supplies the information needed for a rendering template (eg, pres slide for PresElement)');
+ rootDocument?: DOCt = new DocInfo('document that stores the data for compound template documents.');
color?: STRt = new StrInfo('foreground color data doc', false);
hidden?: BOOLt = new BoolInfo('whether the document is not rendered by its collection', false);
backgroundColor?: STRt = new StrInfo('background color for data doc', false);
@@ -1139,8 +1139,8 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { ...(options || {}) });
}
- export function PresElementBoxDocument() {
- return Prototypes.get(DocumentType.PRESELEMENT);
+ export function PresSlideDocument() {
+ return Prototypes.get(DocumentType.PRESSLIDE);
}
export function DataVizDocument(url: string, options?: DocumentOptions, overwriteDoc?: Doc) {
@@ -1148,7 +1148,7 @@ export namespace Docs {
}
export function AnnoPaletteDocument(options?: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.ANNOPALETTE), new List([Doc.MyStickers]), { ...(options || {}) });
+ return InstanceFromProto(Prototypes.get(DocumentType.ANNOPALETTE), new List([...(Doc.MyStickers ? [Doc.MyStickers] : [])]), { ...(options || {}) });
}
export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 35dc5f1c7..1887c1716 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -148,12 +148,12 @@ export class CurrentUserUtils {
{ title: "Idea", backgroundColor: "pink", icon: "lightbulb" , _layout_showTitle: "title"},
{ title: "Topic", backgroundColor: "lightblue", icon: "book-open" , _layout_showTitle: "title"}];
-
+ const metanote = DocCast(doc.emptyMetaNote);
const reqdNoteList = [...reqdTempOpts.map(opts => {
const reqdOpts = {...opts, isSystem:true, width:200, layout_autoHeight: true, layout_fitWidth: true};
const noteTemp = tempNotes ? DocListCast(tempNotes.data).find(fdoc => fdoc.title === opts.title): undefined;
return DocUtils.AssignOpts(noteTemp, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts));
- }), DocCast(doc.emptyMetaNote), ... DocListCast(tempNotes?.data).filter(note => !reqdTempOpts.find(reqd => reqd.title === note.title))];
+ }), ...(metanote ? [metanote]:[]), ... DocListCast(tempNotes?.data).filter(note => !reqdTempOpts.find(reqd => reqd.title === note.title))];
const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true };
return DocUtils.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
@@ -211,7 +211,7 @@ export class CurrentUserUtils {
const fontBox = (opts:DocumentOptions, fieldKey:string) => Docs.Create.FontIconDocument({ layout:FontIconBox.LayoutString(fieldKey), _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts });
const makeIconTemplate = (name: DocumentType | string | undefined, templateField: string, opts:DocumentOptions) => {
- const title = "icon" + (name ? "_" + name : "");
+ const title = "icon" + (name ? name[0].toUpperCase()+name.slice(1) : "");
const curIcon = DocCast(templateIconsDoc[title]);
const creator = (() => { switch (opts.iconTemplate) {
case DocumentType.IMG : return imageBox;
@@ -424,7 +424,10 @@ pie title Minerals in my tap water
const standardOps = (key:string) => ({ title : "Untitled "+ key, _layout_fitWidth: false, isSystem: true, "dragFactory_count": 0, cloneFieldFilter: new List<string>(["isSystem"]) });
emptyThings.forEach(
thing => DocUtils.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, thing.scripts, thing.funcs));
- DocCast(Doc.UserDoc().emptyMetaNote).title = "MetaNote"; // hack: metanotes are used a template, so 'untitled metaNote' is an awkward name
+ const metanote = DocCast(Doc.UserDoc().emptyMetaNote);
+ if (metanote) {
+ metanote.title = "MetaNote"; // hack: metanotes are used a template, so 'untitled metaNote' is an awkward name
+ }
return [
{ toolTip: "Tap or drag to create a note", title: "Note", icon: "sticky-note", dragFactory: doc.emptyNote as Doc, clickFactory: DocCast(doc.emptyNote)},
@@ -490,7 +493,7 @@ pie title Minerals in my tap water
{ title: "Tools", toolTip: "Tools", target: this.setupToolsBtnPanel(doc, "myTools"), icon: "wrench", },
{ title: "Imports", toolTip: "Imports ⌘I", target: this.setupImportSidebar(doc, "myImports"), icon: "upload", },
{ title: "Closed", toolTip: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), icon: "archive", hidden: true }, // this doc is hidden from the Sidebar, but it's still being used in MyFilesystem which ignores the hidden field
- { title: "Shared", toolTip: "Shared Docs", target: Doc.MySharedDocs, icon: "users", funcs: {badgeValue: badgeValue}},
+ { title: "Shared", toolTip: "Shared Docs", target: Doc.MySharedDocs??Doc.UserDoc(), icon: "users", funcs: {badgeValue: badgeValue}},
{ title: "Trails", toolTip: "Trails ⌘R", target: Doc.UserDoc(), icon: "pres-trail", funcs: {target: getActiveDashTrails}},
{ title: "Image Grouper", toolTip: "Image Grouper", target: this.setupImageGrouper(doc, "myImageGrouper"), icon: "folder-open", hidden: false },
{ title: "Faces", toolTip: "Unique Faces", target: this.setupFaceCollection(doc, "myFaceCollection"), icon: "face-smile", hidden: false },
@@ -551,7 +554,6 @@ pie title Minerals in my tap water
const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc, allTools?.length ? allTools[0]:undefined);
const userTools = allTools && allTools?.length > 1 ? allTools[1]:undefined;
const userBtns = CurrentUserUtils.setupUserDocumentCreatorButtons(doc, userTools);
- // doc.myUserBtns = new PrefetchProxy(userBtns);
const reqdToolOps:DocumentOptions = {
title: "My Tools", isSystem: true, ignoreClick: true, layout_boxShadow: "0 0",
layout_explainer: "This is a palette of documents that can be created.", _layout_dontCenter: "y",
@@ -975,7 +977,10 @@ pie title Minerals in my tap water
};
DocUtils.AssignDocField(doc, "mySharedDocs", opts => Docs.Create.TreeDocument([], opts, sharingDocumentId + "layout", sharingDocumentId), sharedDocOpts, undefined, sharedScripts);
- if (!Doc.GetProto(DocCast(doc.mySharedDocs)).data_dashboards) Doc.GetProto(DocCast(doc.mySharedDocs)).data_dashboards = new List<Doc>();
+ const sharedDocs = DocCast(doc.mySharedDocs);
+ if (sharedDocs) {
+ if (!Doc.GetProto(sharedDocs).data_dashboards) Doc.GetProto(sharedDocs).data_dashboards = new List<Doc>();
+ }
}
/// Import option on the left side button panel
@@ -1000,9 +1005,9 @@ pie title Minerals in my tap water
static updateUserDocument(docIn: Doc, sharingDocumentId: string, linkDatabaseId: string) {
const doc = docIn;
DocUtils.AssignDocField(doc, "globalGroupDatabase", () => Docs.Prototypes.MainGroupDocument(), {});
- reaction(() => DateCast(DocCast(doc.globalGroupDatabase).data_modificationDate),
+ reaction(() => DateCast(DocCast(doc.globalGroupDatabase)?.data_modificationDate),
async () => {
- const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
+ const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase)?.data);
const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(ClientUtils.CurrentUserEmail())) || [];
SetCachedGroups(["Guest", ...(mygroups?.map(g => StrCast(g.title))??[])]);
}, { fireImmediately: true });
@@ -1058,11 +1063,13 @@ pie title Minerals in my tap water
SelectionManager.DeselectAll(); // this forces SelectionManager implementation to copy over to DocumentView's API. This also triggers the LinkManager to be created
- Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards);
- Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs);
- Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyRecentlyClosed);
+ if (Doc.MyFilesystem) {
+ Doc.MyDashboards && Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards);
+ Doc.MyDashboards && Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards);
+ Doc.MyRecentlyClosed && Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyRecentlyClosed);
+ }
- Doc.GetProto(DocCast(Doc.UserDoc().emptyWebpage)).data = new WebField("https://www.wikipedia.org");
+ DocCast(Doc.UserDoc().emptyWebpage) && (Doc.GetProto(DocCast(Doc.UserDoc().emptyWebpage)!).data = new WebField("https://www.wikipedia.org"));
DocServer.CacheNeedsUpdate() && setTimeout(UPDATE_SERVER_CACHE, 2500);
setInterval(UPDATE_SERVER_CACHE, 120000);
@@ -1142,7 +1149,7 @@ pie title Minerals in my tap water
const file = input.files?.[0];
if (file?.type === 'application/zip' || file?.type === 'application/x-zip-compressed') {
const doc = await Doc.importDocument(file);
- const list = Cast(Doc.MyImports.data, listSpec(Doc), null);
+ const list = Cast(Doc.MyImports?.data, listSpec(Doc), null);
doc instanceof Doc && list?.splice(0, 0, doc);
} else if (input.files && input.files.length !== 0) {
const disposer = OverlayView.ShowSpinner();
@@ -1150,7 +1157,7 @@ pie title Minerals in my tap water
if (results.length !== input.files?.length) {
alert("Error uploading files - possibly due to unsupported file types");
}
- const list = Cast(Doc.MyImports.data, listSpec(Doc), null);
+ const list = Cast(Doc.MyImports?.data, listSpec(Doc), null);
list?.splice(0, 0, ...results);
disposer();
} else {
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 2eef3da0e..dcef4a4fe 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -364,7 +364,7 @@ export namespace DictationManager {
case 'nested collection':return Docs.Create.FreeformDocument([], {});
} // prettier-ignore
})();
- created && Doc.AddDocToList(target.dataDoc, Doc.LayoutFieldKey(target.Document), created);
+ created && Doc.AddDocToList(target.dataDoc, Doc.LayoutDataKey(target.Document), created);
}
}
},
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 5ce005811..ad57c2a62 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -183,7 +183,7 @@ export class DocumentManager {
static _howl: Howl;
static playAudioAnno(doc: Doc) {
- const anno = Cast(doc[Doc.LayoutFieldKey(doc) + '_audioAnnotations'], listSpec(AudioField), null)?.lastElement();
+ const anno = Cast(doc[Doc.LayoutDataKey(doc) + '_audioAnnotations'], listSpec(AudioField), null)?.lastElement();
if (anno) {
this._howl?.stop();
if (anno instanceof AudioField) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index e2e4c0fe4..a66e6998b 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -234,29 +234,27 @@ export namespace DragManager {
dropDoc instanceof Doc && CreateLinkToActiveAudio(() => dropDoc);
return dropDoc;
};
- const finishDrag = async (e: DragCompleteEvent) => {
+ const finishDrag = (e: DragCompleteEvent) => {
const { docDragData } = e;
setTimeout(() => dragData.dragEnding?.());
onDropCompleted?.(e); // glr: optional additional function to be called - in this case with presentation trails
if (docDragData && !docDragData.droppedDocuments.length) {
docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
- docDragData.droppedDocuments = (
- await Promise.all(
- dragData.draggedDocuments.map(async d =>
- !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
- ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result as Doc)
- : docDragData.dropAction === dropActionType.embed
- ? Doc.BestEmbedding(d)
- : docDragData.dropAction === dropActionType.add
- ? d
- : docDragData.dropAction === dropActionType.proto
- ? d[DocData]
- : docDragData.dropAction === dropActionType.copy
- ? (await Doc.MakeClone(d)).clone
- : d
- )
+ docDragData.droppedDocuments = dragData.draggedDocuments
+ .map(d =>
+ !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
+ ? addAudioTag(ScriptCast(d.onDragStart)!.script.run({ this: d }).result as Doc)
+ : docDragData.dropAction === dropActionType.embed
+ ? Doc.BestEmbedding(d)
+ : docDragData.dropAction === dropActionType.add
+ ? d
+ : docDragData.dropAction === dropActionType.proto
+ ? d[DocData]
+ : docDragData.dropAction === dropActionType.copy
+ ? Doc.MakeClone(d).clone
+ : d
)
- ).filter(d => d);
+ .filter(d => d);
![dropActionType.same, dropActionType.proto].includes(StrCast(docDragData.dropAction) as dropActionType) &&
docDragData.droppedDocuments
// .filter(drop => !drop.dragOnlyWithinContainer || ['embed', 'copy'].includes(docDragData.dropAction as any))
@@ -364,7 +362,7 @@ export namespace DragManager {
};
}
- async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number; y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) {
+ function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number; y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) {
const dropArgs = {
cancelable: true, // allows preventDefault() to be called to cancel the drop
bubbles: true,
@@ -380,7 +378,7 @@ export namespace DragManager {
};
target.dispatchEvent(new CustomEvent<DropEvent>('dashPreDrop', dropArgs));
UndoManager.StartTempBatch(); // run drag/drop in temp batch in case drop is not allowed (so we can undo any intermediate changes)
- await finishDrag?.(complete);
+ finishDrag?.(complete);
UndoManager.EndTempBatch(target.dispatchEvent(new CustomEvent<DropEvent>('dashOnDrop', dropArgs))); // event return val is true unless the event preventDefault() is called
options?.dragComplete?.(complete);
endDrag?.();
@@ -566,11 +564,11 @@ export namespace DragManager {
const targClassName = e.target instanceof HTMLElement && typeof e.target.className === 'string' ? e.target.className : '';
if (['lm_tab', 'lm_title_wrap', 'lm_tabs', 'lm_header'].includes(targClassName) && docDragData.draggedDocuments.length === 1) {
if (!startWindowDragTimer) {
- startWindowDragTimer = setTimeout(async () => {
+ startWindowDragTimer = setTimeout(() => {
startWindowDragTimer = undefined;
docDragData.dropAction = docDragData.userDropAction || dropActionType.same;
AbortDrag();
- await finishDrag?.(new DragCompleteEvent(true, docDragData));
+ finishDrag?.(new DragCompleteEvent(true, docDragData));
DragManager.StartWindowDrag?.(e, docDragData.droppedDocuments, aborted => {
if (!aborted && (docDragData?.dropAction === dropActionType.move || docDragData?.dropAction === dropActionType.same)) {
docDragData.removeDocument?.(docDragData?.draggedDocuments[0]);
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 7d3f63448..b6b111930 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -3,7 +3,7 @@ import { DocData, DocLayout } from '../../fields/DocSymbols';
import { ObjectField } from '../../fields/ObjectField';
import { RichTextField } from '../../fields/RichTextField';
import { ComputedField, ScriptField } from '../../fields/ScriptField';
-import { StrCast } from '../../fields/Types';
+import { DocCast, StrCast } from '../../fields/Types';
import { ImageField } from '../../fields/URLField';
import { Docs, DocumentOptions } from '../documents/Documents';
import { DocumentType } from '../documents/DocumentTypes';
@@ -23,7 +23,7 @@ import { ScriptingGlobals } from './ScriptingGlobals';
*/
function makeTemplate(doc: Doc, first: boolean = true): boolean {
const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
- if (layoutDoc.layout instanceof Doc) {
+ if (DocCast(layoutDoc.layout)) {
return true; // its already a template
}
const layout = StrCast(layoutDoc.layout).match(/fieldKey={'[^']*'}/)?.[0];
@@ -68,7 +68,7 @@ export function MakeTemplate(doc: Doc) {
* Makes a draggable button or image that will create a template doc Instance
*/
export function makeUserTemplateButtonOrImage(doc: Doc, image?: string) {
- const layoutDoc = doc; // doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
+ const layoutDoc = doc;
if (layoutDoc.type !== DocumentType.FONTICON) {
!layoutDoc.isTemplateDoc && makeTemplate(layoutDoc);
}
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index 9c32ca25a..eae3460b3 100644
--- a/src/client/util/Import & Export/ImageUtils.ts
+++ b/src/client/util/Import & Export/ImageUtils.ts
@@ -15,7 +15,7 @@ export namespace ImageUtils {
export const AssignImgInfo = (document: Doc, data?: Upload.InspectionResults) => {
if (data) {
data.nativeWidth && (document._height = (NumCast(document._width) * data.nativeHeight) / data.nativeWidth);
- const field = '$' + Doc.LayoutFieldKey(document);
+ const field = '$' + Doc.LayoutDataKey(document);
document[`${field}_nativeWidth`] = data.nativeWidth;
document[`${field}_nativeHeight`] = data.nativeHeight;
document[`${field}_path`] = data.source;
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 0e67dcfaa..6081c3fae 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -43,13 +43,13 @@ export class LinkFollower {
};
public static traverseLink(link: Opt<Doc>, sourceDoc: Doc, finished?: () => void, traverseBacklink?: boolean) {
- const getView = (doc: Doc) => DocumentView.getFirstDocumentView(DocCast(doc.layout_unrendered ? doc.annotationOn : doc));
- const isAnchor = (source: Doc, anchor: Doc) => Doc.AreProtosEqual(anchor, source) || Doc.AreProtosEqual(anchor.annotationOn as Doc, source);
+ const getView = (doc: Doc) => DocumentView.getFirstDocumentView(DocCast(doc.layout_unrendered ? doc.annotationOn : doc)!);
+ const isAnchor = (source?: Doc, anchor?: Doc) => Doc.AreProtosEqual(anchor, source) || Doc.AreProtosEqual(DocCast(anchor?.annotationOn), source);
const linkDocs = link ? [link] : Doc.Links(sourceDoc);
- const fwdLinks = linkDocs.filter(l => isAnchor(sourceDoc, l.link_anchor_1 as Doc)); // link docs where 'sourceDoc' is link_anchor_1
- const backLinks = linkDocs.filter(l => isAnchor(sourceDoc, l.link_anchor_2 as Doc)); // link docs where 'sourceDoc' is link_anchor_2
- const fwdLinkWithoutTargetView = fwdLinks.find(l => !getView(DocCast(l.link_anchor_2)));
- const backLinkWithoutTargetView = backLinks.find(l => !getView(DocCast(l.link_anchor_1)));
+ const fwdLinks = linkDocs.filter(l => isAnchor(sourceDoc, DocCast(l.link_anchor_1))); // link docs where 'sourceDoc' is link_anchor_1
+ const backLinks = linkDocs.filter(l => isAnchor(sourceDoc, DocCast(l.link_anchor_2))); // link docs where 'sourceDoc' is link_anchor_2
+ const fwdLinkWithoutTargetView = fwdLinks.find(l => !DocCast(l.link_anchor_2) || !getView(DocCast(l.link_anchor_2)!));
+ const backLinkWithoutTargetView = backLinks.find(l => !DocCast(l.link_anchor_1) || !getView(DocCast(l.link_anchor_1)!));
const linkWithoutTargetDoc = traverseBacklink === undefined ? (fwdLinkWithoutTargetView ?? backLinkWithoutTargetView) : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
const linkDocList = linkWithoutTargetDoc && !sourceDoc.followAllLinks ? [linkWithoutTargetDoc] : traverseBacklink === undefined ? fwdLinks.concat(backLinks) : traverseBacklink ? backLinks : fwdLinks;
const followLinks = sourceDoc.followLinkToggle || sourceDoc.followAllLinks ? linkDocList : linkDocList.slice(0, 1);
@@ -60,17 +60,17 @@ export class LinkFollower {
return false;
}
followLinks.forEach(async linkDoc => {
- const target = (
+ const target = DocCast(
sourceDoc === linkDoc.link_anchor_1
? linkDoc.link_anchor_2
: sourceDoc === linkDoc.link_anchor_2
? linkDoc.link_anchor_1
- : Doc.AreProtosEqual(sourceDoc, linkDoc.link_anchor_1 as Doc) || Doc.AreProtosEqual((linkDoc.link_anchor_1 as Doc).annotationOn as Doc, sourceDoc)
+ : Doc.AreProtosEqual(sourceDoc, DocCast(linkDoc.link_anchor_1)) || Doc.AreProtosEqual(DocCast(DocCast(linkDoc.link_anchor_1)?.annotationOn), sourceDoc)
? linkDoc.link_anchor_2
: linkDoc.link_anchor_1
- ) as Doc;
- const srcAnchor: Doc = Doc.getOppositeAnchor(linkDoc, target) ?? sourceDoc;
+ );
if (target) {
+ const srcAnchor = Doc.getOppositeAnchor(linkDoc, target) ?? sourceDoc;
const doFollow = (canToggle?: boolean) => {
const toggleTarget = canToggle && BoolCast(sourceDoc.followLinkToggle);
const options: FocusViewOptions = {
@@ -102,14 +102,16 @@ export class LinkFollower {
if (srcAnchor.followLinkLocation === OpenWhere.inParent) {
const sourceDocParent = DocCast(sourceDoc.embedContainer);
if (target.embedContainer instanceof Doc && target.embedContainer !== sourceDocParent) {
- Doc.RemoveDocFromList(target.embedContainer, Doc.LayoutFieldKey(target.embedContainer), target);
+ Doc.RemoveDocFromList(target.embedContainer, Doc.LayoutDataKey(target.embedContainer), target);
movedTarget = true;
}
- if (!DocListCast(sourceDocParent[Doc.LayoutFieldKey(sourceDocParent)]).includes(target)) {
- Doc.AddDocToList(sourceDocParent, Doc.LayoutFieldKey(sourceDocParent), target);
- movedTarget = true;
+ if (sourceDocParent) {
+ if (!DocListCast(sourceDocParent[Doc.LayoutDataKey(sourceDocParent)]).includes(target)) {
+ Doc.AddDocToList(sourceDocParent, Doc.LayoutDataKey(sourceDocParent), target);
+ movedTarget = true;
+ }
+ Doc.SetContainer(target, sourceDocParent);
}
- Doc.SetContainer(target, sourceDocParent);
}
const moveTo = [NumCast(sourceDoc.x) + NumCast(sourceDoc.followLinkXoffset), NumCast(sourceDoc.y) + NumCast(sourceDoc.followLinkYoffset)];
if (srcAnchor.followLinkXoffset !== undefined && moveTo[0] !== target.x) {
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 3f98ab3c4..344e2e4c0 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -136,7 +136,7 @@ export class LinkManager {
true
);
FieldLoader.ServerLoadStatus.message = 'links';
- this.addLinkDB(Doc.LinkDBDoc());
+ Doc.LinkDBDoc() && this.addLinkDB(Doc.LinkDBDoc()!);
}
public createlink_relationshipLists = () => {
@@ -148,16 +148,21 @@ export class LinkManager {
public addLink(linkDoc: Doc, checkExists = false) {
Doc.AddDocToList(Doc.UserDoc(), 'links', linkDoc);
- if (!checkExists || !DocListCast(Doc.LinkDBDoc().data).includes(linkDoc)) {
- Doc.AddDocToList(Doc.LinkDBDoc(), 'data', linkDoc);
- // eslint-disable-next-line no-use-before-define
- setTimeout(UPDATE_SERVER_CACHE, 100);
+ if (Doc.LinkDBDoc()) {
+ if (!checkExists || !DocListCast(Doc.LinkDBDoc()!.data).includes(linkDoc)) {
+ Doc.AddDocToList(Doc.LinkDBDoc()!, 'data', linkDoc);
+ // eslint-disable-next-line no-use-before-define
+ setTimeout(UPDATE_SERVER_CACHE, 100);
+ }
}
}
public deleteLink(linkDoc: Doc) {
- const ret = Doc.RemoveDocFromList(Doc.LinkDBDoc(), 'data', linkDoc);
- linkDoc.$link_anchor_1 = linkDoc.$link_anchor_2 = undefined;
- return ret;
+ if (Doc.LinkDBDoc()) {
+ const ret = Doc.RemoveDocFromList(Doc.LinkDBDoc()!, 'data', linkDoc);
+ linkDoc.$link_anchor_1 = linkDoc.$link_anchor_2 = undefined;
+ return ret;
+ }
+ return false;
}
public deleteAllLinksOnAnchor(anchor: Doc) {
LinkManager.Instance.relatedLinker(anchor).forEach(LinkManager.Instance.deleteLink);
@@ -178,8 +183,8 @@ export class LinkManager {
}
const dirLinks = Array.from(anchor[DocData][DirectLinks]).filter(l => Doc.GetProto(anchor) === anchor[DocData] || ['1', '2'].includes(LinkManager.anchorIndex(l, anchor) as '0' | '1' | '2'));
- const anchorRoot = DocCast(anchor.rootDocument, anchor); // template Doc fields store annotations on the topmost root of a template (not on themselves since the template layout items are only for layout)
- const annos = DocListCast(anchorRoot[Doc.LayoutFieldKey(anchor) + '_annotations']);
+ const anchorRoot = DocCast(anchor.rootDocument, anchor)!; // template Doc fields store annotations on the topmost root of a template (not on themselves since the template layout items are only for layout)
+ const annos = DocListCast(anchorRoot[Doc.LayoutDataKey(anchor) + '_annotations']);
return Array.from(
annos.reduce((set, anno) => {
if (!processed.includes(anno)) {
@@ -242,11 +247,13 @@ export function UPDATE_SERVER_CACHE() {
.filter(doc => doc instanceof Doc)
.map(doc => doc as Doc);
const references = new Set<Doc>(prototypes);
+ DocCast(Doc.UserDoc().myLinkDatabase) && references.add(DocCast(Doc.UserDoc().myLinkDatabase)!); // prevent crawling through link database here -- see below
Doc.FindReferences(Doc.UserDoc(), references, undefined);
- DocListCast(DocCast(Doc.UserDoc().myLinkDatabase).data).forEach(link => {
- if (!references.has(DocCast(link.link_anchor_1)) && !references.has(DocCast(link.link_anchor_2))) {
- Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myLinkDatabase), 'data', link);
- Doc.AddDocToList(Doc.MyRecentlyClosed, undefined, link);
+
+ DocListCast(DocCast(Doc.UserDoc().myLinkDatabase)?.data).forEach(link => {
+ if (DocCast(link.link_anchor_1) && !references.has(DocCast(link.link_anchor_1)!) && DocCast(link.link_anchor_2) && !references.has(DocCast(link.link_anchor_2)!)) {
+ DocCast(Doc.UserDoc().myLinkDatabase) && Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myLinkDatabase)!, 'data', link);
+ Doc.MyRecentlyClosed && Doc.AddDocToList(Doc.MyRecentlyClosed, undefined, link);
}
});
LinkManager.Instance.userLinkDBs.forEach(linkDb => Doc.FindReferences(linkDb, references, undefined));
@@ -257,10 +264,10 @@ export function UPDATE_SERVER_CACHE() {
cacheDocumentIds = newCacheUpdate;
// print out cached docs
- //Doc.MyDockedBtns.linearView_IsOpen && console.log('Set cached docs = ');
- // const isFiltered = filtered.filter(doc => !Doc.IsSystem(doc));
- // const strings = isFiltered.map(doc => StrCast(doc.title) + ' ' + (Doc.IsDataProto(doc) ? '(data)' : '(embedding)'));
- //Doc.MyDockedBtns.linearView_IsOpen && strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
+ Doc.MyDockedBtns?.linearView_IsOpen && console.log('Set cached docs = ');
+ const isFiltered = filtered.filter(doc => !Doc.IsSystem(doc));
+ const strings = isFiltered.map(doc => StrCast(doc.title) + ' ' + (Doc.IsDataProto(doc) ? '(data)' : '(embedding)'));
+ Doc.MyDockedBtns?.linearView_IsOpen && strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
rp.post(ClientUtils.prepend('/setCacheDocumentIds'), {
body: {
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 733eae5f4..e4adcaa7e 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -9,7 +9,7 @@ export namespace SearchUtil {
export type HighlightingResult = { [id: string]: { [key: string]: string[] } };
export function SearchCollection(collectionDoc: Opt<Doc>, queryIn: string, matchKeyNames: boolean, onlyKeys?: string[]) {
- const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
+ const blockedTypes = [DocumentType.PRESSLIDE, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
const blockedKeys = matchKeyNames
? []
: Object.entries(DocOptions)
@@ -21,7 +21,7 @@ export namespace SearchUtil {
const results = new ObservableMap<Doc, string[]>();
if (collectionDoc) {
- const docs = DocListCast(collectionDoc[Doc.LayoutFieldKey(collectionDoc)]);
+ const docs = DocListCast(collectionDoc[Doc.LayoutDataKey(collectionDoc)]);
// eslint-disable-next-line @typescript-eslint/ban-types
const docIDs: String[] = [];
SearchUtil.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => {
@@ -77,7 +77,7 @@ export namespace SearchUtil {
// eslint-disable-next-line no-loop-func
docs.filter(d => d && !visited.includes(d)).forEach(d => {
visited.push(d);
- const fieldKey = Doc.LayoutFieldKey(d);
+ const fieldKey = Doc.LayoutDataKey(d);
const annos = !Field.toString(Doc.LayoutField(d) as FieldType).includes('CollectionView');
const data = d[annos ? fieldKey + '_annotations' : fieldKey];
data && newarray.push(...DocListCast(data));
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 88f1f3260..9e79fd870 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -578,7 +578,7 @@ export class SettingsManager extends React.Component<object> {
<div className="settings-username" style={{ color: SettingsManager.userBackgroundColor }}>
{ClientUtils.CurrentUserEmail()}
</div>
- <Button text={Doc.GuestDashboard ? 'Exit' : 'Log Out'} type={Type.TERT} color={SettingsManager.userVariantColor} onClick={() => window.location.assign(ClientUtils.prepend('/logout'))} />
+ <Button text={Doc.GuestDashboard ? 'Exit' : 'Log Out'} type={Type.TERT} color={SettingsManager.userVariantColor} background={SettingsManager.userColor} onClick={() => window.location.assign(ClientUtils.prepend('/logout'))} />
</div>
</div>
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 45c80c6f7..1ecd82dbc 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -48,7 +48,7 @@ export function DocComponent<P extends DocComponentProps>() {
* This Doc inherits from the dataDoc, and may or may not inherit (or be) the layoutDoc.
*/
get rootDoc() {
- return DocCast(this.Document.rootDocument, this.Document);
+ return DocCast(this.Document.rootDocument, this.Document)!;
}
/**
@@ -64,7 +64,7 @@ export function DocComponent<P extends DocComponentProps>() {
* This may or may not inherit from the data doc.
*/
@computed get layoutDoc() {
- return this._props.LayoutTemplateString ? this.Document : Doc.Layout(this.Document, this._props.LayoutTemplate?.());
+ return this._props.LayoutTemplateString ? this.Document : Doc.LayoutDoc(this.Document, this._props.LayoutTemplate?.());
}
/**
@@ -115,7 +115,7 @@ export function ViewBoxBaseComponent<P extends FieldViewProps>() {
* consider: Document, layoutDoc, dataDoc
*/
get rootDoc() {
- return DocCast(this.Document.rootDocument, this.Document);
+ return DocCast(this.Document.rootDocument, this.Document)!;
}
/**
* This is the document being rendered by the React component. In the
@@ -189,7 +189,7 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() {
* other Doc options (Document, layoutDoc, dataDoc)
*/
@computed get rootDoc() {
- return DocCast(this.Document.rootDocument, this.Document);
+ return DocCast(this.Document.rootDocument, this.Document)!;
}
/**
* This is the document being rendered. It may be a template so it may or may no inherit from the data doc.
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 6f2f051cd..8a850467a 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -303,7 +303,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
DocumentView.Selected().forEach(dv => {
dv.layoutDoc._layout_showTags = !showing;
if (e.shiftKey)
- DocListCast(dv.Document[Doc.LayoutFieldKey(dv.Document) + '_annotations']).forEach(doc => {
+ DocListCast(dv.Document[Doc.LayoutDataKey(dv.Document) + '_annotations']).forEach(doc => {
if (doc.face) doc.hidden = showing;
});
});
@@ -479,13 +479,13 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
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);
+ const trail = DocCast(anchor.presentationTrail) ?? (DocCast(Doc.UserDoc().emptyTrail) ? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail)!, true) : undefined);
if (trail !== anchor.presentationTrail) {
- DocUtils.MakeLink(anchor, trail, { link_relationship: 'link trail' });
+ trail && DocUtils.MakeLink(anchor, trail, { link_relationship: 'link trail' });
anchor.presentationTrail = trail;
}
Doc.ActivePresentation = trail;
- this._props.views().lastElement()?._props.addDocTab(trail, OpenWhere.replaceRight);
+ trail && this._props.views().lastElement()?._props.addDocTab(trail, OpenWhere.replaceRight);
}
e.stopPropagation();
};
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index c3b3f9d0c..848a77004 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -182,7 +182,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
return targetView?.ComponentView?.annotationKey || (targetView?.ComponentView?.fieldKey ?? 'data');
}
@computed get targetDocChildren() {
- return [...DocListCast(this.Document?.[this.targetDocChildKey] || Doc.ActiveDashboard?.data), ...DocListCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_sidebar'])];
+ return [...DocListCast(this.Document?.[this.targetDocChildKey] || Doc.ActiveDashboard?.data), ...DocListCast(this.Document[Doc.LayoutDataKey(this.Document) + '_sidebar'])];
}
@computed get rangeFilters() {
@@ -236,7 +236,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
if (facetVal instanceof RichTextField || typeof facetVal === 'string') rtFields++;
facetVal !== undefined && valueSet.add(Field.toString(facetVal as FieldType));
(facetVal === true || facetVal === false) && valueSet.add(Field.toString(!facetVal));
- const fieldKey = Doc.LayoutFieldKey(t);
+ const fieldKey = Doc.LayoutDataKey(t);
const annos = !Field.toString(Doc.LayoutField(t) as FieldType).includes('CollectionView');
DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
annos && DocListCast(t[fieldKey + '_sidebar']).forEach(newdoc => newarray.push(newdoc));
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 94c023330..948beec74 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -239,7 +239,7 @@ export class KeyManager {
break;
case 'i':
{
- const importBtn = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MyImports);
+ const importBtn = DocListCast(Doc.MyLeftSidebarMenu?.data).find(d => d.target === Doc.MyImports);
if (importBtn) {
MainView.Instance.selectLeftSidebarButton(importBtn);
}
@@ -247,7 +247,7 @@ export class KeyManager {
break;
case 's':
{
- const trailsBtn = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MyTrails);
+ const trailsBtn = DocListCast(Doc.MyLeftSidebarMenu?.data).find(d => d.target === Doc.MyTrails);
if (trailsBtn) {
MainView.Instance.selectLeftSidebarButton(trailsBtn);
}
@@ -257,7 +257,7 @@ export class KeyManager {
if (DocumentView.Selected().length === 1 && DocumentView.Selected()[0].ComponentView?.search) {
DocumentView.Selected()[0].ComponentView?.search?.('', false, false);
} else {
- const searchBtn = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MySearcher);
+ const searchBtn = DocListCast(Doc.MyLeftSidebarMenu?.data).find(d => d.target === Doc.MySearcher);
if (searchBtn) {
MainView.Instance.selectLeftSidebarButton(searchBtn);
}
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 6854476e2..425f5b6bb 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -341,7 +341,7 @@ export class InkStrokeProperties {
*/
snapControl = (inkView: DocumentView, controlIndex: number) => {
const inkDoc = inkView.Document;
- const ink = Cast(inkDoc[Doc.LayoutFieldKey(inkDoc)], InkField)?.inkData;
+ const ink = Cast(inkDoc[Doc.LayoutDataKey(inkDoc)], InkField)?.inkData;
if (ink) {
const screenDragPt = inkView.ComponentView?.ptToScreen?.(ink[controlIndex]);
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 1293aa9d0..2e6b477e9 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -118,9 +118,9 @@ export class InkTranscription extends React.Component {
const times: number[] = [];
validInks
- .filter(i => Cast(i[Doc.LayoutFieldKey(i)], InkField))
+ .filter(i => Cast(i[Doc.LayoutDataKey(i)], InkField))
.forEach(i => {
- const d = Cast(i[Doc.LayoutFieldKey(i)], InkField, null);
+ const d = Cast(i[Doc.LayoutDataKey(i)], InkField, null);
const inkStroke = DocumentView.getDocumentView(i)?.ComponentView as InkingStroke;
strokes.push(d.inkData.map(pd => inkStroke.ptToScreen({ X: pd.X, Y: pd.Y })));
times.push(DateCast(i.author_date).getDate().getTime());
@@ -382,7 +382,7 @@ export class InkTranscription extends React.Component {
render() {
return (
- <div className="ink-transcription" style={{pointerEvents: 'none'}}>
+ <div className="ink-transcription" style={{ pointerEvents: 'none' }}>
<div className="math-editor" ref={this.setMathRef}></div>
<div className="text-editor" ref={this.setTextRef}></div>
</div>
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index de2c7cd09..0eb21b943 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -136,7 +136,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
return this.SetLightboxDoc(
doc,
undefined,
- [...DocListCast(doc[Doc.LayoutFieldKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...this._future].sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)),
+ [...DocListCast(doc[Doc.LayoutDataKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...this._future].sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)),
layoutTemplate
);
};
@@ -187,7 +187,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
saved: this._savedState,
});
if (this._docTarget) {
- const fieldKey = Doc.LayoutFieldKey(this._docTarget);
+ const fieldKey = Doc.LayoutDataKey(this._docTarget);
const contents = [...DocListCast(this._docTarget[fieldKey]), ...DocListCast(this._docTarget[fieldKey + '_annotations'])];
const links = Doc.Links(this._docTarget)
.map(link => Doc.getOppositeAnchor(link, this._docTarget!)!)
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 22725a2b9..e4bbb1c0f 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -61,7 +61,7 @@ import { FootnoteView } from './nodes/formattedText/FootnoteView';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import { SummaryView } from './nodes/formattedText/SummaryView';
import { ImportElementBox } from './nodes/importBox/ImportElementBox';
-import { PresBox, PresElementBox } from './nodes/trails';
+import { PresBox, PresSlideBox } from './nodes/trails';
import { FaceRecognitionHandler } from './search/FaceRecognitionHandler';
import { SearchBox } from './search/SearchBox';
import { StickerPalette } from './smartdraw/StickerPalette';
@@ -135,7 +135,7 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' };
AudioBox,
RecordingBox,
PresBox,
- PresElementBox,
+ PresSlideBox,
SearchBox,
ImageLabelBox,
FaceCollectionBox,
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index be6e2fecb..ad6bb09c7 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -93,7 +93,7 @@ export class MainView extends ObservableReactComponent<object> {
@observable private _dashUIWidth: number = 0; // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row)
@observable private _dashUIHeight: number = 0; // height of entire main dashboard region including top menu buttons
@observable private _panelContent: string = 'none';
- @observable private _sidebarContent: Doc = Doc.MyLeftSidebarPanel;
+ @observable private _sidebarContent?: Doc = Doc.MyLeftSidebarPanel;
@observable private _leftMenuFlyoutWidth: number = 0;
@computed get _hideUI() {
return SnappingManager.HideUI || (this.mainDoc && this.mainDoc._type_collection !== CollectionViewType.Docking);
@@ -173,7 +173,7 @@ export class MainView extends ObservableReactComponent<object> {
views => views.length > 1 && document.activeElement instanceof HTMLElement && document.activeElement?.blur()
);
reaction(
- () => Doc.MyDockedBtns.linearView_IsOpen,
+ () => Doc.MyDockedBtns?.linearView_IsOpen,
open => SnappingManager.SetPrintToConsole(!!open)
);
const scriptTag = document.createElement('script');
@@ -199,7 +199,7 @@ export class MainView extends ObservableReactComponent<object> {
ele.outerHTML = '';
}, 1000);
}
- this._sidebarContent.proto = undefined;
+ this._sidebarContent && (this._sidebarContent.proto = undefined);
if (!MainView.Live) {
DocServer.setLivePlaygroundFields([
'dataTransition',
@@ -636,8 +636,10 @@ export class MainView extends ObservableReactComponent<object> {
openPresentation = (pres: Doc) => {
if (pres.type === DocumentType.PRES) {
CollectionDockingView.AddSplit(pres, OpenWhereMod.right, undefined, PresBox.PanelName);
- Doc.MyTrails && (Doc.ActivePresentation = pres);
- Doc.AddDocToList(Doc.MyTrails, 'data', pres);
+ if (Doc.MyTrails) {
+ Doc.ActivePresentation = pres;
+ Doc.AddDocToList(Doc.MyTrails, 'data', pres);
+ }
this.closeFlyout();
}
};
@@ -645,16 +647,16 @@ export class MainView extends ObservableReactComponent<object> {
@action
createNewFolder = async () => {
const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true });
- Doc.AddDocToList(Doc.MyFilesystem, 'data', folder);
+ Doc.MyFilesystem && Doc.AddDocToList(Doc.MyFilesystem, 'data', folder);
};
waitForDoubleClick = () => (SnappingManager.ExploreMode ? 'never' : undefined);
headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1);
mainScreenToLocalXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.topOfMainDocContent, 1);
- addHeaderDoc = (docs: Doc | Doc[]) => toList(docs).reduce((done, doc) => Doc.AddDocToList(this.headerBarDoc, 'data', doc), true);
- removeHeaderDoc = (docs: Doc | Doc[]) => toList(docs).reduce((done, doc) => Doc.RemoveDocFromList(this.headerBarDoc, 'data', doc), true);
+ addHeaderDoc = (docs: Doc | Doc[]) => toList(docs).reduce((done, doc) => !!this.headerBarDoc && Doc.AddDocToList(this.headerBarDoc, 'data', doc), true);
+ removeHeaderDoc = (docs: Doc | Doc[]) => toList(docs).reduce((done, doc) => !!this.headerBarDoc && Doc.RemoveDocFromList(this.headerBarDoc, 'data', doc), true);
@computed get headerBarDocView() {
- return (
+ return !this.headerBarDoc ? null : (
<div className="mainView-headerBar" style={{ height: this.headerBarDocHeight() }}>
<DocumentView
key="headerBarDoc"
@@ -792,16 +794,16 @@ export class MainView extends ObservableReactComponent<object> {
<div key="flyout" className="mainView-libraryFlyout-out">
{this.docButtons}
</div>
- ) : (
+ ) : !this._sidebarContent ? null : (
<div key="libFlyout" className="mainView-libraryFlyout" style={{ minWidth: this._leftMenuFlyoutWidth, width: this._leftMenuFlyoutWidth }}>
<div className="mainView-contentArea">
<DocumentView
- Document={DocCast(this._sidebarContent.proto, this._sidebarContent)}
+ Document={DocCast(this._sidebarContent?.proto, this._sidebarContent)!}
addDocument={undefined}
addDocTab={DocumentViewInternal.addDocTabFunc}
pinToPres={DocumentView.PinDoc}
containerViewPath={returnEmptyDocViewList}
- styleProvider={this._sidebarContent.proto === Doc.MyDashboards || this._sidebarContent.proto === Doc.MyFilesystem || this._sidebarContent.proto === Doc.MyTrails ? DashboardStyleProvider : DefaultStyleProvider}
+ 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}
@@ -821,7 +823,7 @@ export class MainView extends ObservableReactComponent<object> {
}
@computed get leftMenuPanel() {
- return (
+ return !Doc.MyLeftSidebarMenu ? null : (
<div key="menu" className="mainView-leftMenuPanel" style={{ background: SnappingManager.userBackgroundColor, display: DocumentView.LightboxDoc() ? 'none' : undefined }}>
<DocumentView
Document={Doc.MyLeftSidebarMenu}
@@ -861,7 +863,7 @@ export class MainView extends ObservableReactComponent<object> {
break;
default:
this._leftMenuFlyoutWidth = this._leftMenuFlyoutWidth || 250;
- this._sidebarContent.proto = DocCast(button.target);
+ this._sidebarContent && (this._sidebarContent.proto = DocCast(button.target));
SnappingManager.SetLastPressedBtn(button[Id]);
}
}
@@ -875,8 +877,10 @@ export class MainView extends ObservableReactComponent<object> {
*/
addHotKey = (hotKey: string) => {
const filterIcons = DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter);
- const menuDoc = CurrentUserUtils.setupContextMenuBtn(CurrentUserUtils.filterBtnDesc(ToTagName(hotKey), 'question'), filterIcons);
- Doc.AddToFilterHotKeys(menuDoc);
+ if (filterIcons) {
+ const menuDoc = CurrentUserUtils.setupContextMenuBtn(CurrentUserUtils.filterBtnDesc(ToTagName(hotKey), 'question'), filterIcons);
+ Doc.AddToFilterHotKeys(menuDoc);
+ }
};
@computed get mainInnerContent() {
@@ -941,13 +945,13 @@ export class MainView extends ObservableReactComponent<object> {
closeFlyout = action(() => {
SnappingManager.SetLastPressedBtn('');
this._panelContent = 'none';
- this._sidebarContent.proto = undefined;
+ this._sidebarContent && (this._sidebarContent.proto = undefined);
this._leftMenuFlyoutWidth = 0;
});
- remButtonDoc = (docs: Doc | Doc[]) => toList(docs).reduce((flg: boolean, doc) => flg && !doc.dragOnlyWithinContainer && Doc.RemoveDocFromList(Doc.MyDockedBtns, 'data', doc), true);
+ remButtonDoc = (docs: Doc | Doc[]) => toList(docs).reduce((flg: boolean, doc) => flg && !doc.dragOnlyWithinContainer && !!Doc.MyDockedBtns && Doc.RemoveDocFromList(Doc.MyDockedBtns!, 'data', doc), true);
moveButtonDoc = (docs: Doc | Doc[], targetCol: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(docs) && addDocument(docs);
- addButtonDoc = (docs: Doc | Doc[]) => toList(docs).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.MyDockedBtns, 'data', doc), true);
+ addButtonDoc = (docs: Doc | Doc[]) => toList(docs).reduce((flg: boolean, doc) => flg && !!Doc.MyDockedBtns && Doc.AddDocToList(Doc.MyDockedBtns!, 'data', doc), true);
buttonBarXf = () => {
if (!this._docBtnRef.current) return Transform.Identity();
diff --git a/src/client/views/PinFuncs.ts b/src/client/views/PinFuncs.ts
index d756830da..805dd9a49 100644
--- a/src/client/views/PinFuncs.ts
+++ b/src/client/views/PinFuncs.ts
@@ -70,13 +70,13 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
pinProps.pinData.dataview ||
pinProps.pinData.poslayoutview ||
pinProps?.activeFrame !== undefined;
- const fkey = Doc.LayoutFieldKey(targetDoc);
+ const fkey = Doc.LayoutDataKey(targetDoc);
if (pinProps.pinData.dataview) {
pinDoc.config_usePath = targetDoc[fkey + '_usePath'];
pinDoc.config_data = Field.Copy(targetDoc[fkey]);
}
if (pinProps.pinData.dataannos) {
- const fieldKey = '$' + Doc.LayoutFieldKey(targetDoc) + +'_annotations';
+ const fieldKey = '$' + Doc.LayoutDataKey(targetDoc) + +'_annotations';
pinDoc.config_annotations = new List<Doc>(DocListCast(targetDoc[fieldKey]).filter(doc => !doc.layout_unrendered));
}
if (pinProps.pinData.inkable) {
@@ -87,7 +87,7 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
}
if (pinProps.pinData.scrollable) pinDoc.config_scrollTop = targetDoc._layout_scrollTop;
if (pinProps.pinData.clippable) {
- const fieldKey = Doc.LayoutFieldKey(targetDoc);
+ const fieldKey = Doc.LayoutDataKey(targetDoc);
pinDoc.config_clipWidth = targetDoc[fieldKey + '_clipWidth'];
}
if (pinProps.pinData.datarange) {
@@ -127,7 +127,7 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
}
if (pinProps.pinData.temporal) {
pinDoc.config_clipStart = targetDoc._layout_currentTimecode;
- const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], NumCast(targetDoc.config_clipStart) + 0.1);
+ const duration = NumCast(pinDoc[`${Doc.LayoutDataKey(pinDoc)}_duration`], NumCast(targetDoc.config_clipStart) + 0.1);
pinDoc.config_clipEnd = NumCast(pinDoc.config_clipStart) + NumCast(targetDoc.clipEnd, duration);
}
}
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 6778a6691..66936c64c 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -447,7 +447,7 @@ export class PropertiesButtons extends React.Component {
};
render() {
- const layoutField = this.selectedDoc?.[Doc.LayoutFieldKey(this.selectedDoc)];
+ const layoutField = this.selectedDoc?.[Doc.LayoutDataKey(this.selectedDoc)];
const isText = DocumentView.Selected().lastElement()?.ComponentView instanceof FormattedTextBox;
const isInk = this.selectedDoc?.layout_isSvg;
const isImage = layoutField instanceof ImageField;
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 7fcb15afe..e7186c0e3 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -525,7 +525,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
let permission;
if (this.layoutDocAcls) {
if (target[DocAcl][userKey]) permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
- else if (target.embedContainer) permission = StrCast(Doc.GetProto(DocCast(target.embedContainer))[userKey]);
+ else if (DocCast(target.embedContainer)) permission = StrCast(Doc.GetProto(DocCast(target.embedContainer)!)[userKey]);
else permission = StrCast(Doc.GetProto(target)?.[userKey]);
} else permission = StrCast(target[userKey]);
individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
@@ -546,7 +546,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
if (this.layoutDocAcls) {
if (target[DocAcl][groupKey]) {
permission = HierarchyMapping.get(target[DocAcl][groupKey])?.name;
- } else if (target.embedContainer) permission = StrCast(Doc.GetProto(DocCast(target.embedContainer))[groupKey]);
+ } else if (DocCast(target.embedContainer)) permission = StrCast(Doc.GetProto(DocCast(target.embedContainer)!)[groupKey]);
else permission = StrCast(Doc.GetProto(target)?.[groupKey]);
} else permission = StrCast(target[groupKey]);
groupTableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission!, false));
@@ -1921,7 +1921,9 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
const selectedItem: boolean = PresBox.Instance.selectedArray.size > 0;
const type = [DocumentType.AUDIO, DocumentType.VID].includes(DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as DocumentType)
? (DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as DocumentType)
- : PresBox.targetRenderedDoc(PresBox.Instance.activeItem)?.type;
+ : PresBox.Instance.activeItem
+ ? PresBox.targetRenderedDoc(PresBox.Instance.activeItem)?.type
+ : undefined;
return (
<div className="propertiesView" style={{ width: this._props.width }}>
<div className="propertiesView-sectionTitle" style={{ width: this._props.width }}>
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index e8876f96f..9110e198f 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -129,7 +129,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
// prettier-ignore
switch (property.split(':')[0]) {
case StyleProp.TreeViewIcon: {
- const img = ImageCast(doc?.icon ?? doc?.[doc ? Doc.LayoutFieldKey(doc) : ""]);
+ const img = ImageCast(doc?.icon ?? doc?.[doc ? Doc.LayoutDataKey(doc) : ""]);
if (img) {
const ext = extname(img.url.href);
const url = doc?.icon ? img.url.href : img.url.href.replace(ext, '_s' + ext);
@@ -164,7 +164,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
return undefined;
case StyleProp.DocContents: return undefined;
case StyleProp.WidgetColor: return isAnnotated ? Colors.LIGHT_BLUE : 'dimgrey';
- case StyleProp.Opacity: return componentView?.isUnstyledView?.() ? 1 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null));
+ case StyleProp.Opacity: return componentView?.isUnstyledView?.() ? 1 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null) ?? null);
case StyleProp.FontColor: return StrCast(doc?.[fieldKey + 'fontColor'], isCaption ? lightOrDark(backgroundCol()) : StrCast(Doc.UserDoc().fontColor, color()));
case StyleProp.FontSize: return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(Doc.UserDoc().fontSize));
case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(Doc.UserDoc().fontFamily));
@@ -248,13 +248,13 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
: 0;
case StyleProp.BackgroundColor: {
if (SnappingManager.LastPressedBtn === doc?.[Id]) return SnappingManager.userColor; // hack to indicate active menu panel item
- const dataKey = doc ? Doc.LayoutFieldKey(doc) : '';
+ const dataKey = doc ? Doc.LayoutDataKey(doc) : '';
const usePath = StrCast(doc?.[dataKey + '_usePath']);
const alternate = usePath.includes(':hover') ? ( isHovering?.() ? '_' + usePath.replace(':hover','') : '') : usePath ? "_" +usePath:usePath;
let docColor:Opt<string> = layoutDoc &&
StrCast(alternate ? layoutDoc['backgroundColor' + alternate]:undefined,
- doc.rootDocument
- ? StrCast(layoutDoc.backgroundColor, StrCast(DocCast(doc.rootDocument).backgroundColor)) // for nested templates: use template's color, then root doc's color
+ DocCast(doc.rootDocument)
+ ? StrCast(layoutDoc.backgroundColor, StrCast(DocCast(doc.rootDocument)!.backgroundColor)) // for nested templates: use template's color, then root doc's color
: layoutDoc === doc
? StrCast(doc.backgroundColor)
: StrCast(StrCast(Doc.GetT(layoutDoc, 'backgroundColor', 'string', true), StrCast(doc.backgroundColor, StrCast(layoutDoc.backgroundColor)) // otherwise, use expanded template coloor, then root doc's color, then template's inherited color
@@ -262,7 +262,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
// prettier-ignore
switch (layoutDoc?.type) {
- case DocumentType.PRESELEMENT: docColor = docColor || ""; break;
+ case DocumentType.PRESSLIDE: docColor = docColor || ""; break;
case DocumentType.PRES: docColor = docColor || 'transparent'; break;
case DocumentType.FONTICON: docColor = boxBackground ? undefined : docColor || SnappingManager.userBackgroundColor; break;
case DocumentType.RTF: docColor = docColor || StrCast(Doc.UserDoc().textBackgroundColor, Colors.LIGHT_GRAY); break;
diff --git a/src/client/views/StyleProviderQuiz.tsx b/src/client/views/StyleProviderQuiz.tsx
index b4a1f53c5..fe76bce28 100644
--- a/src/client/views/StyleProviderQuiz.tsx
+++ b/src/client/views/StyleProviderQuiz.tsx
@@ -67,7 +67,7 @@ export namespace styleProviderQuiz {
newCol.zIndex = 1000;
newCol.forceActive = true;
newCol.quiz = text;
- newCol['$' + Doc.LayoutFieldKey(newCol) + '_transform'] = 'none';
+ newCol['$' + Doc.LayoutDataKey(newCol) + '_transform'] = 'none';
Doc.AddDocToList(img.Document, 'quizBoxes', newCol);
img.addDocument(newCol);
// img._loading = false;
diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx
index 79c4d8af1..40d4f108d 100644
--- a/src/client/views/TagsView.tsx
+++ b/src/client/views/TagsView.tsx
@@ -21,6 +21,7 @@ import { FaceRecognitionHandler } from './search/FaceRecognitionHandler';
import { IconTagBox } from './nodes/IconTagBox';
import { Id } from '../../fields/FieldSymbols';
import { StyleProp } from './StyleProp';
+import { Docs } from '../documents/Documents';
/**
* The TagsView is a metadata input/display panel shown at the bottom of a DocumentView in a freeform collection.
@@ -171,6 +172,7 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
const newEmbeddings = TagItem.allDocsWithTag(this._props.tag).map(doc => Doc.MakeEmbedding(doc));
// Create a new collection and set up configurations.
+ const emptyCol = DocCast(Doc.UserDoc().emptyCollection);
const newCollection = ((doc: Doc) => {
doc.$data = new List<Doc>(newEmbeddings);
doc.$title = this._props.tag;
@@ -182,7 +184,7 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
doc.layout_fitWidth = true;
doc._layout_showTags = true;
return doc;
- })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
+ })(emptyCol ? Doc.MakeCopy(emptyCol, true) : Docs.Create.FreeformDocument([], {}));
newEmbeddings.forEach(embed => Doc.SetContainer(embed, newCollection));
// Add the collection to the tag document's list of associated smart collections.
@@ -342,7 +344,7 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
const tagsList = new Set<string>(StrListCast(this.View.dataDoc.tags));
const chatTagsList = new Set<string>(StrListCast(this.View.dataDoc.tags_chat));
const facesList = new Set<string>(
- DocListCast(this.View.dataDoc[Doc.LayoutFieldKey(this.View.Document) + '_annotations'])
+ DocListCast(this.View.dataDoc[Doc.LayoutDataKey(this.View.Document) + '_annotations'])
.concat(this.View.Document)
.filter(d => d.face)
.map(doc => StrCast(DocCast(doc.face)?.title))
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index cd2c7df1b..814e9a7a0 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -92,7 +92,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps & { Doc: D
private get children(): Doc[] {
const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this._props.Doc.type) as unknown as DocumentType);
if (annotatedDoc) {
- return DocListCast(this._props.Doc[Doc.LayoutFieldKey(this._props.Doc) + '_annotations']);
+ return DocListCast(this._props.Doc[Doc.LayoutDataKey(this._props.Doc) + '_annotations']);
}
return DocListCast(this._props.Doc[this._props.fieldKey]);
}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 2d3f8cb0e..975dc52fe 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -77,7 +77,8 @@ export class CollectionCarouselView extends CollectionSubView() {
focus = (anchor: Doc, options: FocusViewOptions): Opt<number> => {
const docs = DocListCast(this.Document[this.fieldKey]);
if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) {
- const newIndex = anchor.config_carousel_index ?? docs.getIndex(DocCast(anchor.annotationOn, anchor));
+ const annoOn = DocCast(anchor.annotationOn, anchor);
+ const newIndex = NumCast(anchor.config_carousel_index, (annoOn && docs.getIndex(annoOn)) ?? 0);
options.didMove = newIndex !== this.layoutDoc._carousel_index;
options.didMove && (this.layoutDoc._carousel_index = newIndex);
}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 094e61db9..ea6259a32 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -473,11 +473,11 @@ export class CollectionDockingView extends CollectionSubView() {
}
return undefined;
}
- public static async TakeSnapshot(doc: Doc | undefined, clone = false) {
+ public static TakeSnapshot(doc: Doc | undefined, clone = false) {
if (!doc) return undefined;
let json = StrCast(doc.dockingConfig);
if (clone) {
- const cloned = await Doc.MakeClone(doc);
+ const cloned = Doc.MakeClone(doc);
Array.from(cloned.map.entries()).forEach(entry => {
json = json.replace(entry[0], entry[1][Id]);
});
@@ -524,11 +524,11 @@ export class CollectionDockingView extends CollectionSubView() {
this._flush = this._flush ?? UndoManager.StartBatch('tab movement');
const dashDoc = tab.DashDoc;
if (dashDoc && ![DocumentType.PRES].includes(dashDoc.type) && !tab.contentItem.config.props.keyValue) {
- Doc.AddDocToList(Doc.MyHeaderBar, 'data', dashDoc, undefined, undefined, true);
+ Doc.MyHeaderBar && Doc.AddDocToList(Doc.MyHeaderBar, 'data', dashDoc, undefined, undefined, true);
// if you close a tab that is not embedded somewhere else (an embedded Doc can be opened simultaneously in a tab), then add the tab to recently closed
if (dashDoc.embedContainer === this.Document) dashDoc.embedContainer = undefined;
if (!dashDoc.embedContainer) {
- Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', dashDoc, undefined, true, true);
+ Doc.MyRecentlyClosed && Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', dashDoc, undefined, true, true);
Doc.RemoveEmbedding(dashDoc, dashDoc);
}
}
@@ -562,7 +562,7 @@ export class CollectionDockingView extends CollectionSubView() {
_layout_fitWidth: true,
title: `Untitled Tab ${NumCast(dashboard.$myPaneCount)}`,
});
- Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
+ Doc.MyHeaderBar && Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
inheritParentAcls(this.Document, docToAdd, false);
CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
}
@@ -579,7 +579,7 @@ export class CollectionDockingView extends CollectionSubView() {
_freeform_backgroundGrid: true,
title: `Untitled Tab ${NumCast(dashboard.$myPaneCount)}`,
});
- Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
+ Doc.MyHeaderBar && Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
inheritParentAcls(this.dataDoc, docToAdd, false);
CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
}
@@ -668,7 +668,5 @@ ScriptingGlobals.add(
);
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(async function snapshotDashboard() {
- const batch = UndoManager.StartBatch('snapshot');
- await CollectionDockingView.TakeSnapshot(Doc.ActiveDashboard);
- batch.end();
+ undoable(() => CollectionDockingView.TakeSnapshot(Doc.ActiveDashboard), 'snapshot dashboard');
}, 'creates a snapshot copy of a dashboard');
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 173147f64..7f639a11e 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -321,11 +321,10 @@ export class CollectionNoteTakingView extends CollectionSubView() {
return Math.min(maxWidth - CollectionNoteTakingViewColumn.ColumnMargin, width < maxWidth ? width : maxWidth);
};
- // how to get the height of a document. Nothing special here.
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.TemplateDataDocument;
+ const childLayoutDoc = Doc.LayoutDoc(d, this._props.childLayoutTemplate?.());
+ const childDataDoc = d.isTemplateDoc || d.isTemplateForField ? this._props.TemplateDataDocument : undefined;
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);
@@ -566,7 +565,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
@undoBatch
remColumn = (value: SchemaHeaderField) => {
- const colHdrData = Array.from(Cast(this.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
+ const colHdrData = Array.from(Cast(this.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null) ?? []);
if (value) {
const index = colHdrData.indexOf(value);
index !== -1 && colHdrData.splice(index, 1);
@@ -585,7 +584,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
}
return undefined;
});
- const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
+ const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null) ?? []);
const newColWidth = 1 / (this.numGroupColumns + 1);
columnHeaders.push(new SchemaHeaderField(value, undefined, undefined, newColWidth));
value && this.resizeColumns(columnHeaders);
@@ -642,7 +641,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
leftHeader.setWidth(leftHeader.width + movementX / this.availableWidth);
rightHeader.setWidth(rightHeader.width - movementX / this.availableWidth);
const headers = Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null);
- headers.splice(headers.indexOf(leftHeader), 1, leftHeader[Copy]());
+ headers?.splice(headers.indexOf(leftHeader), 1, leftHeader[Copy]());
};
// renderedSections returns a list of all of the JSX elements used (columns and dividers). If the view
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 112510265..9155227dd 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -315,8 +315,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
e.stopPropagation?.();
const layoutFieldKey = StrCast(textBox.fieldKey);
const newDoc = Doc.MakeCopy(textBox.Document, true);
- const dataField = textBox.Document[Doc.LayoutFieldKey(newDoc)];
- newDoc['$' + Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
+ const dataField = textBox.Document[Doc.LayoutDataKey(newDoc)];
+ newDoc['$' + Doc.LayoutDataKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
if (layoutFieldKey !== 'layout' && textBox.Document[layoutFieldKey] instanceof Doc) {
newDoc[layoutFieldKey] = textBox.Document[layoutFieldKey];
}
@@ -340,7 +340,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
isChildButtonContentActive = () => (this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false ? false : undefined);
@observable docRefs = new ObservableMap<Doc, DocumentView>();
- childFitWidth = (doc: Doc) => Cast(this.Document.childLayoutFitWidth, 'boolean', this._props.childLayoutFitWidth?.(doc) ?? Cast(doc.layout_fitWidth, 'boolean', null));
+ childFitWidth = (doc: Doc) => Cast(this.Document.childLayoutFitWidth, 'boolean', this._props.childLayoutFitWidth?.(doc) ?? Cast(doc.layout_fitWidth, 'boolean', null) ?? null);
// 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, trans: () => string, count: number) {
@@ -408,7 +408,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
});
getDocWidth = computedFn((d?: Doc) => () => {
if (!d) return 0;
- const childLayoutDoc = Doc.Layout(d, this._props.childLayoutTemplate?.());
+ const childLayoutDoc = Doc.LayoutDoc(d, this._props.childLayoutTemplate?.());
const maxWidth = this.columnWidth / this.numGroupColumns;
if (!this.layoutDoc._columnsFill && !this.childFitWidth(childLayoutDoc)) {
return Math.min(NumCast(d._width), maxWidth);
@@ -418,8 +418,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
getDocTransition = computedFn((d?: Doc) => () => StrCast(d?.dataTransition));
getDocHeight = computedFn((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.TemplateDataDocument;
+ const childLayoutDoc = Doc.LayoutDoc(d, this._props.childLayoutTemplate?.());
+ const childDataDoc = d.isTemplateDoc || d.isTemplateForField ? this._props.TemplateDataDocument : undefined;
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);
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 66839ba7f..994669734 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -274,7 +274,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
const container = DocCast(this._props.Doc.rootDocument)?.[DocData] ? Doc.GetProto(this._props.Doc) : this._props.Doc;
if (container.isTemplateDoc) {
Doc.MakeMetadataFieldTemplate(created, container);
- return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created);
+ return Doc.AddDocToList(container, Doc.LayoutDataKey(container), created);
}
return this._props.addDocument?.(created) || false;
}
@@ -316,7 +316,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
ref={this._headerRef}
style={{
marginTop: this._props.yMargin,
- width: this._props.columnWidth
+ width: this._props.columnWidth,
}}>
{/* the default bucket (no key value) has a tooltip that describes what it is.
Further, it does not have a color and cannot be deleted. */}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 375c0fe53..bc7d6f897 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -3,7 +3,7 @@ import * as React from 'react';
import * as rp from 'request-promise';
import { ClientUtils, DashColor, returnFalse } from '../../../ClientUtils';
import CursorField from '../../../fields/CursorField';
-import { Doc, DocListCast, GetDocFromUrl, GetHrefFromHTML, Opt, RTFIsFragment, StrListCast } from '../../../fields/Doc';
+import { Doc, DocListCast, expandedFieldName, GetDocFromUrl, GetHrefFromHTML, Opt, RTFIsFragment, StrListCast } from '../../../fields/Doc';
import { AclPrivate, DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
@@ -132,16 +132,23 @@ export function CollectionSubView<X>() {
hasChildDocs = () => this.childLayoutPairs.map(pair => pair.layout);
@computed get childLayoutPairs(): { layout: Doc; data: Doc }[] {
- const { TemplateDataDocument } = this._props;
- const validPairs = this.childDocs
- .map(doc => Doc.GetLayoutDataDocPair(this.Document, !this._props.isAnnotationOverlay ? TemplateDataDocument : undefined, doc))
- .filter(
- pair =>
- // filter out any documents that have a proto that we don't have permissions to
- !pair.layout?.hidden && pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate))
- )
- .filter(pair => !this._filterFunc?.(pair.layout!));
- return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
+ const lastEle = this.DocumentView?.();
+ if (!lastEle?.IsInvalid(this.Document)) {
+ const rootTemplate = lastEle && Doc.LayoutDoc(lastEle.rootDoc).isTemplateDoc && Doc.LayoutDoc(lastEle.rootDoc);
+ const templateFieldKey = rootTemplate &&
+ [expandedFieldName(rootTemplate),
+ ...this._props.docViewPath()
+ .filter(dv => dv.Document.isTemplateForField)
+ .map(dv => dv.Document.title),
+ ].join('_'); // prettier-ignore
+ return this.childDocs
+ .map(doc => Doc.GetLayoutDataDocPair(this.Document, !this._props.isAnnotationOverlay ? this._props.TemplateDataDocument : undefined, doc, templateFieldKey || ""))
+ .filter(pair => // filter out any documents that have a proto that we don't have permissions to
+ !pair.layout?.hidden && pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate)))
+ .filter(pair => !this._filterFunc?.(pair.layout!))
+ .map(({ data, layout }) => ({ data: data!, layout: layout! })); // prettier-ignore
+ }
+ return [];
}
/**
* This is the raw, stored list of children on a collection. If you modify this list, the database will be updated
@@ -161,7 +168,7 @@ export function CollectionSubView<X>() {
};
collectionFilters = () => this._focusFilters ?? StrListCast(this.Document._childFilters);
- collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.Document._childFiltersByRanges, listSpec('string'), []);
+ collectionRangeDocFilters = () => this._focusRangeFilters ?? StrListCast(this.Document._childFiltersByRanges);
// child filters apply to the descendants of the documents in this collection
childDocFilters = () => [...(this._props.childFilters?.().filter(f => ClientUtils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()];
// unrecursive filters apply to the documents in the collection, but no their children. See Utils.noRecursionHack
@@ -178,7 +185,7 @@ export function CollectionSubView<X>() {
rawdocs = [this.dataField];
} else if (Cast(this.dataField, listSpec(Doc), null)) {
// otherwise, if the collection data is a list, then use it.
- rawdocs = Cast(this.dataField, listSpec(Doc), null);
+ rawdocs = DocListCast(this.dataField);
} else if (this.dataField) {
// 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.
@@ -206,7 +213,7 @@ export function CollectionSubView<X>() {
let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), childFiltersByRanges, this.Document).length > 0;
if (notFiltered) {
notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, childFiltersByRanges, this.Document).length > 0;
- const fieldKey = Doc.LayoutFieldKey(d);
+ const fieldKey = Doc.LayoutDataKey(d);
const isAnnotatableDoc = d[fieldKey] instanceof List && !(d[fieldKey] as List<Doc>)?.some(ele => !(ele instanceof Doc));
const docChildDocs = d[isAnnotatableDoc ? fieldKey + '_annotations' : fieldKey];
const sidebarDocs = isAnnotatableDoc && d[fieldKey + '_sidebar'];
@@ -219,7 +226,7 @@ export function CollectionSubView<X>() {
newarray = [];
// eslint-disable-next-line no-loop-func
subDocs.forEach(t => {
- const docFieldKey = Doc.LayoutFieldKey(t);
+ const docFieldKey = Doc.LayoutDataKey(t);
const isSubDocAnnotatable = t[docFieldKey] instanceof List && !(t[docFieldKey] as List<Doc>)?.some(ele => !(ele instanceof Doc));
notFiltered =
notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !childFiltersByRanges.length) || DocUtils.FilterDocs([t], childDocFilters, childFiltersByRanges, d).length));
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 568a08792..4348bc7dc 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -15,7 +15,7 @@ import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { FieldId } from '../../../fields/RefField';
import { ComputedField } from '../../../fields/ScriptField';
-import { Cast, NumCast, StrCast, toList } from '../../../fields/Types';
+import { Cast, DocCast, NumCast, StrCast, toList } from '../../../fields/Types';
import { DocServer } from '../../DocServer';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
import { Docs } from '../../documents/Documents';
@@ -145,7 +145,7 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
select={emptyFunction}
isSelected={returnFalse}
dontRegisterView
- fieldKey={Doc.LayoutFieldKey(this._props.doc)}
+ fieldKey={Doc.LayoutDataKey(this._props.doc)}
addDocument={returnFalse}
moveDocument={returnFalse}
removeDocument={returnFalse}
@@ -173,7 +173,7 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
);
};
render() {
- return this._props.doc.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._props.doc)) || this._props.doc?._type_collection !== CollectionViewType.Freeform ? null : (
+ return this._props.doc.layout !== CollectionView.LayoutString(Doc.LayoutDataKey(this._props.doc)) || this._props.doc?._type_collection !== CollectionViewType.Freeform ? null : (
<div className="miniMap-hidden">
<Popup icon={<FontAwesomeIcon icon="globe-asia" size="lg" />} color={SnappingManager.userVariantColor} type={Type.TERT} onPointerDown={e => e.stopPropagation()} placement="top-end" popup={this.popup} />
</div>
@@ -207,10 +207,10 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
const docs = toList(docIn);
const batch = UndoManager.StartBatch('Pin doc to pres trail');
- const curPres = Doc.ActivePresentation ?? Doc.MakeCopy(Doc.UserDoc().emptyTrail as Doc, true);
+ const curPres = Doc.ActivePresentation ?? (DocCast(Doc.UserDoc().emptyTrail) ? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail)!, true) : Docs.Create.PresDocument({}));
if (!Doc.ActivePresentation) {
- Doc.AddDocToList(Doc.MyTrails, 'data', curPres);
+ Doc.MyTrails && Doc.AddDocToList(Doc.MyTrails, 'data', curPres);
Doc.ActivePresentation = curPres;
}
@@ -236,7 +236,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
pinDoc.treeView_FieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
pinDoc.treeView_ExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
pinDoc.treeView_HideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
- const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], null);
+ const duration = NumCast(doc[`${Doc.LayoutDataKey(pinDoc)}_duration`], null);
if (pinProps.pinViewport) PinDocView(pinDoc, pinProps, anchorDoc ?? doc);
if (!pinProps?.audioRange && duration !== undefined) {
@@ -566,7 +566,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
return false;
};
- getCurrentFrame = () => NumCast(Cast(PresBox.Instance.activeItem.presentation_targetDoc, Doc, null)._currentFrame);
+ getCurrentFrame = () => NumCast(DocCast(PresBox.Instance.activeItem?.presentation_targetDoc)?._currentFrame);
focusFunc = () => {
if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) {
this.tab.header.parent.setActiveContentItem(this.tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 4dc937864..4beb75074 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -159,7 +159,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
return this.Document[DocLayout];
}
@computed get fieldKey() {
- return StrCast(this.Document._treeView_FieldKey, Doc.LayoutFieldKey(this.Document));
+ return StrCast(this.Document._treeView_FieldKey, Doc.LayoutDataKey(this.Document));
}
@computed get childDocs() {
return this.childDocList(this.fieldKey);
@@ -186,7 +186,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
moving: boolean = false;
@undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
if (this.Document !== target && addDoc !== returnFalse) {
- const canAdd1 = (this._props.parentTreeView as TreeView).dropping || !(ComputedField.WithoutComputed(() => FieldValue(this._props.parentTreeView?.Document.data)) instanceof ComputedField);
+ const canAdd1 = (this._props.parentTreeView as TreeView).dropping || !(ComputedField.DisableCompute(() => FieldValue(this._props.parentTreeView?.Document.data)) instanceof ComputedField);
// bcz: this should all be running in a Temp undo batch instead of hackily testing for returnFalse
if (canAdd1 && this._props.removeDoc?.(doc) === true) {
@@ -431,9 +431,9 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
localAdd = (docs: Doc | Doc[]): boolean => {
const innerAdd = (doc: Doc): boolean => {
- const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField;
+ const dataIsComputed = ComputedField.DisableCompute(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField;
const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
- dataIsComputed && Doc.SetContainer(doc, DocCast(this.Document.embedContainer));
+ dataIsComputed && DocCast(this.Document.embedContainer) && Doc.SetContainer(doc, DocCast(this.Document.embedContainer)!);
return added;
};
return toList(docs).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
@@ -511,9 +511,9 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const moveDoc = (docs: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.move(docs, target, addDoc);
const addDoc = (docs: Doc | Doc[], addBefore?: Doc, before?: boolean) => {
const innerAdd = (iDoc: Doc) => {
- const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
+ const dataIsComputed = ComputedField.DisableCompute(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, iDoc, addBefore, before, false, true);
- dataIsComputed && Doc.SetContainer(iDoc, DocCast(this.Document.embedContainer));
+ dataIsComputed && DocCast(this.Document.embedContainer) && Doc.SetContainer(iDoc, DocCast(this.Document.embedContainer)!);
return added;
};
return toList(docs).reduce((flg, iDoc) => flg && innerAdd(iDoc), true as boolean);
@@ -633,7 +633,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
d.zIndex = i;
});
}
- const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
+ const dataIsComputed = ComputedField.DisableCompute(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false);
!dataIsComputed && added && Doc.SetContainer(doc, this.Document);
@@ -883,8 +883,8 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
];
};
childContextMenuItems = () => {
- const customScripts = Cast(this.Document.childContextMenuScripts, listSpec(ScriptField), []);
- const customFilters = Cast(this.Document.childContextMenuFilters, listSpec(ScriptField), []);
+ const customScripts = Cast(this.Document.childContextMenuScripts, listSpec(ScriptField), [])!;
+ const customFilters = Cast(this.Document.childContextMenuFilters, listSpec(ScriptField), [])!;
const icons = StrListCast(this.Document.childContextMenuIcons);
return StrListCast(this.Document.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
};
@@ -1296,7 +1296,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const dentDoc = (editTitle: boolean, newParent: Doc, addAfter: Doc | undefined, parent: TreeView | CollectionTreeView | undefined) => {
if (parent instanceof TreeView && parent._props.treeView.fileSysMode && !newParent.isFolder) return;
- const fieldKey = Doc.LayoutFieldKey(newParent);
+ const fieldKey = Doc.LayoutDataKey(newParent);
if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) {
remove(child);
DocumentView.SetSelectOnLoad(child);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 626538976..842293358 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -275,7 +275,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onChildDoubleClickHandler = () => this._props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
elementFunc = () => this._layoutElements;
viewTransition = () => (this._panZoomTransition ? '' + this._panZoomTransition : undefined);
- panZoomTransition = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null)));
+ panZoomTransition = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : (Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null) ?? null) ?? ''));
fitContentOnce = () => {
const { cx, cy, scale } = this.contentBounds(); // prettier-ignore
this.layoutDoc._freeform_panX = cx;
@@ -388,7 +388,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (options.easeFunc) this.setPresEaseFunc(options.easeFunc);
if (this._lightboxDoc) return undefined;
if (options.pointFocus) return this.focusOnPoint(options);
- const anchorInCollection = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]).includes(anchor);
+ const anchorInCollection = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutDataKey(this.Document)]).includes(anchor);
const anchorInChildViews = this.childLayoutPairs.map(pair => pair.layout).includes(anchor);
if (!anchorInCollection && !anchorInChildViews) {
return undefined;
@@ -1308,7 +1308,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
SnappingManager.TriggerUserPanned();
if (this.layoutDoc._Transform || this.Document.treeView_OutlineMode === TreeViewType.outline) return;
e.stopPropagation();
- const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight);
+ const docHeight = NumCast(this.Document[Doc.LayoutDataKey(this.Document) + '_nativeHeight'], this.nativeHeight);
const scrollable = this.isAnnotationOverlay && NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling + 1e-4;
switch (
!e.ctrlKey && !e.shiftKey && !e.metaKey && !e.altKey ?//
@@ -1489,12 +1489,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
*/
createTextDocCopy = undoable((textBox: FormattedTextBox, below: boolean) => {
const textDoc = DocCast(textBox.Document);
- const newDoc = Doc.MakeCopy(textDoc, true);
- newDoc['$' + Doc.LayoutFieldKey(newDoc)] = undefined; // the copy should not copy the text contents of it source, just the render style
- newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10);
- newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0);
- DocumentView.SetSelectOnLoad(newDoc);
- return this.addDocument?.(newDoc);
+ if (textDoc) {
+ const newDoc = Doc.MakeCopy(textDoc, true);
+ newDoc['$' + Doc.LayoutDataKey(newDoc)] = undefined; // the copy should not copy the text contents of it source, just the render style
+ newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10);
+ newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0);
+ DocumentView.SetSelectOnLoad(newDoc);
+ return this.addDocument?.(newDoc);
+ }
+ return false;
}, 'copied text note');
onKey = (e: KeyboardEvent, textBox: FormattedTextBox) => {
@@ -1741,7 +1744,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (addAsAnnotation) {
if (Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
- Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor);
+ Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), [])?.push(anchor);
} else {
this.dataDoc[this._props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
}
@@ -1985,7 +1988,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
optionItems.push({
description: 'Use Background Color as Default',
event: () => {
- Cast(Doc.UserDoc().emptyCollection, Doc, null).backgroundColor = StrCast(this.layoutDoc.backgroundColor);
+ DocCast(Doc.UserDoc().emptyCollection) && (DocCast(Doc.UserDoc().emptyCollection)!.backgroundColor = StrCast(this.layoutDoc.backgroundColor));
},
icon: 'palette',
});
@@ -2085,7 +2088,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
lightboxPanelHeight = () => Math.max(0, this._props.PanelHeight() - 30);
lightboxScreenToLocal = () => this.ScreenToLocalBoxXf().translate(-15, -15);
onPassiveWheel = (e: WheelEvent) => {
- const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight);
+ const docHeight = NumCast(this.Document[Doc.LayoutDataKey(this.Document) + '_nativeHeight'], this.nativeHeight);
const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling;
this._props.isSelected() && !scrollable && e.preventDefault();
};
@@ -2108,7 +2111,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
</div>
);
}
- transitionFunc = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms ${this._presEaseFunc}` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null)));
+ transitionFunc = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms ${this._presEaseFunc}` : (Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null) ?? null) ?? ''));
get pannableContents() {
this.incrementalRender(); // needs to happen synchronously or freshly typed text documents will flash and miss their first characters
return (
@@ -2385,7 +2388,7 @@ ScriptingGlobals.add(function datavizFromSchema() {
const keys = Cast(view.layoutDoc.schema_columnKeys, listSpec('string'))?.filter(key => key !== 'text');
if (!keys) return;
- const children = DocListCast(view.Document[Doc.LayoutFieldKey(view.Document)]);
+ const children = DocListCast(view.Document[Doc.LayoutDataKey(view.Document)]);
const csvRows = [];
csvRows.push(keys.join(','));
for (let i = 0; i < children.length; i++) {
diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
index c7aa410c6..72485aa86 100644
--- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
@@ -187,7 +187,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
})}>
{FaceRecognitionHandler.UniqueFaceImages(this.Document).map((doc, i) => {
- const [name, type] = ImageCastToNameType(doc[Doc.LayoutFieldKey(doc)]) ?? ['-missing-', '.png'];
+ const [name, type] = ImageCastToNameType(doc[Doc.LayoutDataKey(doc)]) ?? ['-missing-', '.png'];
return (
<div
className="image-wrapper"
diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
index 7c8ccb92d..ff9fb14e7 100644
--- a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
@@ -165,7 +165,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
const imageInfos = this._selectedImages.map(async doc => {
if (!doc.$tags_chat) {
- const url = ImageCastWithSuffix(doc[Doc.LayoutFieldKey(doc)], '_o') ?? '';
+ const url = ImageCastWithSuffix(doc[Doc.LayoutDataKey(doc)], '_o') ?? '';
return imageUrlToBase64(url).then(hrefBase64 =>
!hrefBase64 ? undefined :
gptImageLabel(hrefBase64,'Give three labels to describe this image.').then(labels =>
@@ -310,7 +310,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
{this._displayImageInformation ? (
<div className="image-information-list">
{this._selectedImages.map(doc => {
- const [name, type] = ImageCastToNameType(doc[Doc.LayoutFieldKey(doc)]);
+ const [name, type] = ImageCastToNameType(doc[Doc.LayoutDataKey(doc)]);
return (
<div className="image-information" style={{ borderColor: SettingsManager.userColor }} key={Utils.GenerateGuid()}>
<img
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 5fbf72f39..3cc7c0f2d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -8,7 +8,7 @@ import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSy
import { Id } from '../../../../fields/FieldSymbols';
import { InkData, InkTool } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
-import { Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { GetEffectiveAcl } from '../../../../fields/util';
import { DocUtils } from '../../../documents/DocUtils';
@@ -376,7 +376,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
doc.$title = makeGroup ? 'grouping' : 'nested freeform';
doc._freeform_panX = doc._freeform_panY = 0;
return doc;
- })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
+ })(DocCast(Doc.UserDoc().emptyCollection) ? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyCollection)!, true) : Docs.Create.FreeformDocument([], {}));
newCollection.isSystem = undefined;
newCollection._width = bounds.width || 1; // if width/height are unset/0, then groups won't autoexpand to contain their children
newCollection._height = bounds.height || 1;
@@ -437,7 +437,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
*/
@undoBatch
classifyImages = async () => {
- const groupButton = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MyImageGrouper);
+ const groupButton = DocListCast(Doc.MyLeftSidebarMenu?.data).find(d => d.target === Doc.MyImageGrouper);
if (groupButton) {
this._selectedDocs = this.marqueeSelect(false, DocumentType.IMG);
ImageLabelBoxData.Instance.setData(this._selectedDocs);
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 80116dd2f..8d3947653 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -102,7 +102,7 @@ export class CollectionLinearView extends CollectionSubView() {
getLinkUI = () =>
!DocumentLinksButton.StartLink ? null : (
- <span className="bottomPopup-background" style={{ pointerEvents: 'all' }} onPointerDown={e => e.stopPropagation()}>
+ <span key="-link-ui-" className="bottomPopup-background" style={{ pointerEvents: 'all' }} onPointerDown={e => e.stopPropagation()}>
<span className="bottomPopup-text">
Creating link from:{' '}
<b>
@@ -126,7 +126,7 @@ export class CollectionLinearView extends CollectionSubView() {
);
getCurrentlyPlayingUI = () =>
!DocumentView.CurrentlyPlaying?.length ? null : (
- <span className="bottomPopup-background">
+ <span key="-currently-playing-" className="bottomPopup-background">
<span className="bottomPopup-text">
Currently playing:
{DocumentView.CurrentlyPlaying.map((clip, i) => (
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 5803acca0..82ca96839 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1193,7 +1193,7 @@ export class CollectionSchemaView extends CollectionSubView() {
let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), childFiltersByRanges, this.Document).length > 0;
if (notFiltered) {
notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, childFiltersByRanges, this.Document).length > 0;
- const fieldKey = Doc.LayoutFieldKey(d);
+ const fieldKey = Doc.LayoutDataKey(d);
const isAnnotatableDoc = d[fieldKey] instanceof List && !(d[fieldKey] as List<Doc>)?.some(ele => !(ele instanceof Doc));
const docChildDocs = d[isAnnotatableDoc ? fieldKey + '_annotations' : fieldKey];
const sidebarDocs = isAnnotatableDoc && d[fieldKey + '_sidebar'];
@@ -1206,7 +1206,7 @@ export class CollectionSchemaView extends CollectionSubView() {
newarray = [];
// eslint-disable-next-line no-loop-func
subDocs.forEach(t => {
- const docFieldKey = Doc.LayoutFieldKey(t);
+ const docFieldKey = Doc.LayoutDataKey(t);
const isSubDocAnnotatable = t[docFieldKey] instanceof List && !(t[docFieldKey] as List<Doc>)?.some(ele => !(ele instanceof Doc));
notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !childFiltersByRanges.length) || DocUtils.FilterDocs([t], childDocFilters, childFiltersByRanges, d).length));
DocListCast(t[isSubDocAnnotatable ? docFieldKey + '_annotations' : docFieldKey]).forEach(newdoc => newarray.push(newdoc));
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 173984dc7..8e1edc1ee 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -326,7 +326,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
const field = Cast(this._props.Doc[this._props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
const alts = DocListCast(this._props.Doc[this._props.fieldKey + '_alternates']); // retrieve alternate documents that may be rendered as alternate images
const altpaths = alts
- .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
+ .map(doc => Cast(doc[Doc.LayoutDataKey(doc)], ImageField, null)?.url)
.filter(url => url)
.map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 32afb3d3d..f4beb1004 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -76,7 +76,7 @@ ScriptingGlobals.add(function setBorderColor(color?: string, checkResult?: boole
const selView = selectedViews.lastElement();
const layoutFrameNumber = Cast(selView.containerViewPath?.().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] || defaultBorder();
+ return (contentFrameNumber !== undefined && CollectionFreeFormDocumentView.getStringValues(selView?.Document, contentFrameNumber)[fieldKey]) || defaultBorder();
}
setDefaultBorder(color ?? 'transparent');
selectedViews.forEach(dv => {
@@ -87,7 +87,7 @@ ScriptingGlobals.add(function setBorderColor(color?: string, checkResult?: boole
obj[fieldKey] = color;
CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.Document, obj);
} else {
- const dataKey = Doc.LayoutFieldKey(dv.Document);
+ const dataKey = Doc.LayoutDataKey(dv.Document);
const alternate = (dv.layoutDoc[dataKey + '_usePath'] ? '_' + dv.layoutDoc[dataKey + '_usePath'] : '').replace(':hover', '');
dv.layoutDoc[fieldKey + alternate] = undefined;
dv.dataDoc[fieldKey + alternate] = color;
@@ -133,7 +133,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.Document, obj);
} else {
const colorDoc = dv.isTemplateForField ? dv.layoutDoc : dv.dataDoc; // assigning to a template's compoment field should not assign to the data doc
- const dataKey = Doc.LayoutFieldKey(colorDoc);
+ const dataKey = Doc.LayoutDataKey(colorDoc);
const alternate = (dv.layoutDoc[dataKey + '_usePath'] ? '_' + dv.layoutDoc[dataKey + '_usePath'] : '').replace(':hover', '');
colorDoc[fieldKey + alternate] = color;
}
@@ -158,7 +158,7 @@ ScriptingGlobals.add(function setDefaultTemplate(checkResult?: boolean) {
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
if (checkResult) {
- return DocumentView.Selected().length ? StrCast(DocumentView.SelectedDocs().lastElement().layout_headingColor) : Doc.SharingDoc().headingColor;
+ return DocumentView.Selected().length ? StrCast(DocumentView.SelectedDocs().lastElement().layout_headingColor) : Doc.SharingDoc()?.headingColor;
}
if (DocumentView.Selected().length) {
DocumentView.SelectedDocs().forEach(doc => {
@@ -166,8 +166,11 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole
doc.layout_showTitle = color === 'transparent' ? undefined : StrCast(doc.layout_showTitle, 'title');
});
} else {
- Doc.SharingDoc().headingColor = undefined;
- Doc.GetProto(Doc.SharingDoc()).headingColor = color === 'transparent' ? undefined : color;
+ const sharing = Doc.SharingDoc();
+ if (sharing) {
+ sharing.headingColor = undefined;
+ Doc.GetProto(sharing).headingColor = color === 'transparent' ? undefined : color;
+ }
Doc.UserDoc().layout_showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().layout_showTitle, 'title');
}
return undefined;
@@ -234,30 +237,30 @@ ScriptingGlobals.add(function showFreeform(
setDoc: (doc: Doc) => { doc._freeform_useClusters = !doc._freeform_useClusters; },
}],
['time', {
- checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutFieldKey(doc)+"_sort"]) === "time",
- setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutFieldKey(doc)+"_sort"] === "time" ? doc[Doc.LayoutFieldKey(doc)+"_sort"] = '' : doc[Doc.LayoutFieldKey(doc)+"_sort"] = docSortings.Time}, // prettier-ignore
+ checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutDataKey(doc)+"_sort"]) === "time",
+ setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutDataKey(doc)+"_sort"] === "time" ? doc[Doc.LayoutDataKey(doc)+"_sort"] = '' : doc[Doc.LayoutDataKey(doc)+"_sort"] = docSortings.Time}, // prettier-ignore
}],
['docType', {
- checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutFieldKey(doc)+"_sort"]) === "type",
- setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutFieldKey(doc)+"_sort"] === "type" ? doc[Doc.LayoutFieldKey(doc)+"_sort"] = '' : doc[Doc.LayoutFieldKey(doc)+"_sort"] = docSortings.Type}, // prettier-ignore
+ checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutDataKey(doc)+"_sort"]) === "type",
+ setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutDataKey(doc)+"_sort"] === "type" ? doc[Doc.LayoutDataKey(doc)+"_sort"] = '' : doc[Doc.LayoutDataKey(doc)+"_sort"] = docSortings.Type}, // prettier-ignore
}],
['color', {
- checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutFieldKey(doc)+"_sort"]) === "color",
- setDoc: (doc: Doc, dv: DocumentView) => { doc?.[Doc.LayoutFieldKey(doc)+"_sort"] === "color" ? doc[Doc.LayoutFieldKey(doc)+"_sort"] = '' : doc[Doc.LayoutFieldKey(doc)+"_sort"] = docSortings.Color}, // prettier-ignore
+ checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutDataKey(doc)+"_sort"]) === "color",
+ setDoc: (doc: Doc, dv: DocumentView) => { doc?.[Doc.LayoutDataKey(doc)+"_sort"] === "color" ? doc[Doc.LayoutDataKey(doc)+"_sort"] = '' : doc[Doc.LayoutDataKey(doc)+"_sort"] = docSortings.Color}, // prettier-ignore
}],
['tag', {
- checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutFieldKey(doc)+"_sort"]) === "tag",
- setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutFieldKey(doc)+"_sort"] === "tag" ? doc[Doc.LayoutFieldKey(doc)+"_sort"] = '' : doc[Doc.LayoutFieldKey(doc)+"_sort"] = docSortings.Tag}, // prettier-ignore
+ checkResult: (doc: Doc) => StrCast(doc?.[Doc.LayoutDataKey(doc)+"_sort"]) === "tag",
+ setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutDataKey(doc)+"_sort"] === "tag" ? doc[Doc.LayoutDataKey(doc)+"_sort"] = '' : doc[Doc.LayoutDataKey(doc)+"_sort"] = docSortings.Tag}, // prettier-ignore
}],
['reverse', {
- checkResult: (doc: Doc) => BoolCast(doc?.[Doc.LayoutFieldKey(doc)+"_sort_reverse"]),
- setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutFieldKey(doc)+"_sort_reverse"] = !doc[Doc.LayoutFieldKey(doc)+"_sort_reverse"]; },
+ checkResult: (doc: Doc) => BoolCast(doc?.[Doc.LayoutDataKey(doc)+"_sort_reverse"]),
+ setDoc: (doc: Doc, dv: DocumentView) => { doc[Doc.LayoutDataKey(doc)+"_sort_reverse"] = !doc[Doc.LayoutDataKey(doc)+"_sort_reverse"]; },
}],
['toggle-chat', {
checkResult: (doc: Doc) => SnappingManager.ChatVisible,
setDoc: (doc: Doc, dv: DocumentView) => {
if (SnappingManager.ChatVisible){
- doc[Doc.LayoutFieldKey(doc)+"_sort"] = '';
+ doc[Doc.LayoutDataKey(doc)+"_sort"] = '';
SnappingManager.SetChatVisible(false);
} else {
SnappingManager.SetChatVisible(true);
diff --git a/src/client/views/newlightbox/NewLightboxView.tsx b/src/client/views/newlightbox/NewLightboxView.tsx
index b060fc0b6..87cef01d2 100644
--- a/src/client/views/newlightbox/NewLightboxView.tsx
+++ b/src/client/views/newlightbox/NewLightboxView.tsx
@@ -77,7 +77,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
NewLightboxView.SetNewLightboxDoc(
doc,
undefined,
- [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...(NewLightboxView._future ?? [])].sort(
+ [...DocListCast(doc[Doc.LayoutDataKey(doc)]), ...DocListCast(doc[Doc.LayoutDataKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...(NewLightboxView._future ?? [])].sort(
(a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)
),
layoutTemplate
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index e0d59cc9d..d4c512342 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -396,7 +396,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
returnFalse,
action(() => {
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);
+ const textField = Doc.LayoutDataKey(newDoc);
const newDocData = newDoc[DocData];
newDocData[`${textField}_recordingSource`] = this.dataDoc;
newDocData[`${textField}_recordingStart`] = ComputedField.MakeFunction(`this.${textField}_recordingSource.${this.fieldKey}_recordingStart`);
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 53a3d3631..940c4cb99 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -71,7 +71,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
{ key: 'freeform_panY' },
]; // fields that are configured to be animatable using animation frames
public static animStringFields = ['backgroundColor', 'borderColor', 'color', 'fillColor']; // fields that are configured to be animatable using animation frames
- public static animDataFields = (doc: Doc) => (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames
+ public static animDataFields = (doc: Doc) => (Doc.LayoutDataKey(doc) ? [Doc.LayoutDataKey(doc)] : []); // fields that are configured to be animatable using animation frames
public static from(dv?: DocumentView): CollectionFreeFormDocumentView | undefined {
return dv?._props.reactParent instanceof CollectionFreeFormDocumentView ? dv._props.reactParent : undefined;
}
@@ -187,7 +187,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}
public static gotoKeyFrame(doc: Doc, newFrame: number) {
if (doc) {
- const childDocs = DocListCast(doc[Doc.LayoutFieldKey(doc)]);
+ const childDocs = DocListCast(doc[Doc.LayoutDataKey(doc)]);
const currentFrame = Cast(doc._currentFrame, 'number', null);
if (currentFrame === undefined) {
doc._currentFrame = 0;
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 117eb05f8..c80e8ebc1 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -352,7 +352,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const getFrom = DocCast(this.layoutDoc.dataViz_asSchema);
if (!getFrom?.schema_columnKeys) return undefined;
const keys = StrListCast(getFrom?.schema_columnKeys).filter(key => key !== 'text');
- const children = DocListCast(getFrom?.[Doc.LayoutFieldKey(getFrom)]);
+ const children = DocListCast(getFrom?.[Doc.LayoutDataKey(getFrom)]);
const current: { [key: string]: string }[] = [];
children
.filter(child => child)
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 6f004bed3..504c1491e 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -188,8 +188,8 @@ export class DocumentContentsView extends ObservableReactComponent<DocumentConte
this._props.LayoutTemplate?.() ||
(this._props.LayoutTemplateString && this._props.Document) ||
(this._props.layoutFieldKey && StrCast(this._props.Document[this._props.layoutFieldKey]) && this._props.Document) ||
- Doc.Layout(this._props.Document, DocCast(this._props.Document[this._props.layoutFieldKey]));
- return Doc.expandTemplateLayout(template, this._props.Document);
+ Doc.LayoutDoc(this._props.Document, DocCast(this._props.Document[this._props.layoutFieldKey]));
+ return Doc.expandTemplateLayout(template, this._props.Document, this._props.layoutFieldKey);
}
CreateBindings(onClick: Opt<ScriptField>, onInput: Opt<ScriptField>): JsxBindings {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d88d8c44d..521934152 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -169,9 +169,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
}
componentDidMount() {
- runInAction(() => {
- this._mounted = true;
- });
+ runInAction(() => (this._mounted = true));
this.setupHandlers();
this._disposers.contentActive = reaction(
() =>
@@ -183,16 +181,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
: Doc.ActiveTool !== InkTool.None || SnappingManager.CanEmbed || this.rootSelected() || this.Document.forceActive || this._componentView?.isAnyChildContentActive?.() || this._props.isContentActive()
? true
: undefined,
- active => {
- this._isContentActive = active;
- },
+ active => (this._isContentActive = active),
{ fireImmediately: true }
);
this._disposers.pointerevents = reaction(
() => this.style(this.Document, StyleProp.PointerEvents) as Property.PointerEvents | undefined,
- pointerevents => {
- this._pointerEvents = pointerevents;
- },
+ pointerevents => (this._pointerEvents = pointerevents),
{ fireImmediately: true }
);
}
@@ -305,7 +299,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
};
const clickFunc = this.onClickFunc?.()?.script ? () => (this.onClickFunc?.()?.script.run(scriptProps, console.log).result as Opt<{ select: boolean }>)?.select && this._props.select(false) : undefined;
if (!clickFunc) {
- // 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
+ // onDragStart implies a button doc that we don't want to select when clicking.
if (this.layoutDoc.onDragStart && !(e.ctrlKey || e.button > 0)) stopPropagate = false;
preventDefault = false;
}
@@ -502,7 +496,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
const items = this._props.styleProvider?.(this.Document, this._props, StyleProp.ContextMenuItems) as ContextMenuProps[];
items?.forEach(item => ContextMenu.Instance.addItem(item));
- const customScripts = Cast(this.Document.contextMenuScripts, listSpec(ScriptField), []);
+ const customScripts = Cast(this.Document.contextMenuScripts, listSpec(ScriptField), [])!;
StrListCast(this.Document.contextMenuLabels).forEach((label, i) =>
cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.Document, scriptContext: this._props.scriptContext }), icon: 'sticky-note' })
);
@@ -536,9 +530,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
zorderItems.push({
description: !this.layoutDoc._keepZDragged ? 'Keep ZIndex when dragged' : 'Allow ZIndex to change when dragged',
event: undoable(
- action(() => {
- this.layoutDoc._keepZWhenDragged = !this.layoutDoc._keepZWhenDragged;
- }),
+ action(() => (this.layoutDoc._keepZWhenDragged = !this.layoutDoc._keepZWhenDragged)),
'set zIndex drag'
),
icon: 'hand-point-up',
@@ -663,7 +655,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
.scale(this.viewingAiEditor() ? (this._props.PanelHeight() || 1) / this.aiContentsHeight() : 1);
onClickFunc = () => (this.disableClickScriptFunc ? undefined : this.onClickHdlr);
setHeight = (height: number) => { !this._props.suppressSetHeight && (this.layoutDoc._height = Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), height + 2 * NumCast(this.Document.borderWidth))); } // prettier-ignore
- setContentView = action((view: ViewBoxInterface<FieldViewProps>) => { this._componentView = view; }); // prettier-ignore
+ setContentView = action((view: ViewBoxInterface<FieldViewProps>) => (this._componentView = view));
isContentActive = (): boolean | undefined => this._isContentActive;
childFilters = () => [...this._props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)];
@@ -763,6 +755,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
}
widgetOverlayFunc = () => (this.widgetDecorations ? this.widgetOverlay : null);
viewingAiEditor = () => (this._props.showAIEditor && this._componentView?.componentAIView?.() !== undefined ? this.aiEditor : null);
+ _contentsRef = React.createRef<DocumentContentsView>();
@computed get viewBoxContents() {
TraceMobx();
const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString;
@@ -778,6 +771,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
}}>
<DocumentContentsView
{...this._props}
+ ref={this._contentsRef}
layoutFieldKey={StrCast(this.Document.layout_fieldKey, 'layout')}
pointerEvents={this.contentPointerEvents}
setContentViewBox={this.setContentView}
@@ -812,8 +806,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
captionStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => this._props?.styleProvider?.(doc, props, property + ':caption');
fieldsDropdown = (placeholder: string) => (
<div
- ref={r => { r && runInAction(() => (this._titleDropDownInnerWidth = DivWidth(r)));}} // prettier-ignore
- onPointerDown={action(() => { this._changingTitleField = true; })} // prettier-ignore
+ ref={action((r:HTMLDivElement|null) => r && (this._titleDropDownInnerWidth = DivWidth(r)))} // prettier-ignore
+ onPointerDown={action(() => (this._changingTitleField = true))}
style={{ width: 'max-content', background: SnappingManager.userBackgroundColor, color: SnappingManager.userColor, transformOrigin: 'left', transform: `scale(${this.titleHeight / 30 /* height of Dropdown */})` }}>
<FieldsDropdown
Doc={this.Document}
@@ -826,7 +820,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
}
this._changingTitleField = false;
})}
- menuClose={action(() => { this._changingTitleField = false; })} // prettier-ignore
+ menuClose={action(() => (this._changingTitleField = false))}
/>
</div>
);
@@ -842,7 +836,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
const background = StrCast(
this.layoutDoc.layout_headingColor,
// StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor,
- StrCast(Doc.SharingDoc().headingColor, SnappingManager.userBackgroundColor)
+ StrCast(Doc.SharingDoc()?.headingColor, SnappingManager.userBackgroundColor)
// )
);
const dropdownWidth = this._titleRef.current?._editing || this._changingTitleField ? Math.max(10, (this._titleDropDownInnerWidth * this.titleHeight) / 30) : 0;
@@ -1028,7 +1022,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
root: Doc
) {
const effectDirection = (presEffectDoc?.presentation_effectDirection ?? presEffectDoc?.followLinkAnimDirection) as PresEffectDirection;
- const duration = Cast(presEffectDoc?.presentation_transition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null));
+ const duration = Cast(presEffectDoc?.presentation_transition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null) ?? null);
const effectProps = {
left: effectDirection === PresEffectDirection.Left,
right: effectDirection === PresEffectDirection.Right,
@@ -1143,7 +1137,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
public static GetDocImage(doc?: Doc) {
return DocumentView.getDocumentView(doc)
?.ComponentView?.updateIcon?.()
- .then(() => ImageCast(doc!.icon, ImageCast(doc![Doc.LayoutFieldKey(doc!)])));
+ .then(() => ImageCast(doc!.icon, ImageCast(doc![Doc.LayoutDataKey(doc!)])));
}
public get displayName() { return 'DocumentView(' + (this.Document?.title??"") + ')'; } // prettier-ignore
@@ -1181,6 +1175,24 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
@observable public TagPanelHeight = 0;
@observable public TagPanelEditing = false;
+ /**
+ * Tests whether the component Doc being rendered matches the Doc that this view thinks its rendering.
+ * When switching the layout_fieldKey, component views may react before the internal DocumentView has re-rendered.
+ * If this happens, the component view may try to write invalid data, such as when expanding a template (which
+ * depends on the DocumentView being in synch with the subcomponents).
+ * @param renderDoc sub-component Doc being rendered
+ * @returns boolean whether sub-component Doc is in synch with the layoutDoc that this view thinks its rendering
+ */
+ IsInvalid = (renderDoc?: Doc): boolean => {
+ const docContents = this._docViewInternal?._contentsRef.current;
+ return !(
+ (!renderDoc ||
+ (docContents?.layoutDoc === renderDoc && //
+ !this.docViewPath().some(dv => dv.IsInvalid()))) &&
+ docContents?._props.layoutFieldKey === this.Document.layout_fieldKey
+ );
+ };
+
@computed get showTags() {
return this.Document._layout_showTags || this._props.showTags;
}
@@ -1250,7 +1262,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
!BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentView.removeView(this);
}
- public set IsSelected(val) { runInAction(() => { this._selected = val; }); } // prettier-ignore
+ public set IsSelected(val) { runInAction(() => (this._selected = val)) } // prettier-ignore
public get IsSelected() { return this._selected; } // prettier-ignore
public get IsContentActive(){ return this._docViewInternal?.isContentActive(); } // prettier-ignore
public get topMost() { return this._props.renderDepth === 0; } // prettier-ignore
@@ -1262,7 +1274,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
public get HasAIEditor() { return !!this._docViewInternal?._componentView?.componentAIView?.(); } // prettier-ignore
get LayoutFieldKey() {
- return Doc.LayoutFieldKey(this.Document, this._props.LayoutTemplateString);
+ return Doc.LayoutDataKey(this.Document, this._props.LayoutTemplateString);
}
@computed get layout_fitWidth() {
@@ -1316,10 +1328,10 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
public toggleNativeDimensions = () => this._docViewInternal && this.Document.type !== DocumentType.INK && Doc.toggleNativeDimensions(this.layoutDoc, this.NativeDimScaling() ?? 1, this._props.PanelWidth(), this._props.PanelHeight());
- public iconify(finished?: () => void, animateTime?: number) {
+ public iconify = action((finished?: () => void, animateTime?: number) => {
this.ComponentView?.updateIcon?.();
const animTime = this._docViewInternal?.animateScaleTime();
- runInAction(() => { this._docViewInternal && animateTime !== undefined && (this._docViewInternal._animateScaleTime = animateTime); }); // prettier-ignore
+ this._docViewInternal && animateTime !== undefined && (this._docViewInternal._animateScaleTime = animateTime);
const finalFinished = action(() => {
finished?.();
this._docViewInternal && (this._docViewInternal._animateScaleTime = animTime);
@@ -1329,12 +1341,12 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
this.switchViews(true, 'icon', finalFinished);
if (layoutFieldKey && layoutFieldKey !== 'layout' && layoutFieldKey !== 'layout_icon') this.Document.deiconifyLayout = layoutFieldKey.replace('layout_', '');
} else {
- const deiconifyLayout = Cast(this.Document.deiconifyLayout, 'string', null);
+ const deiconifyLayout = StrCast(this.Document.deiconifyLayout);
this.switchViews(!!deiconifyLayout, deiconifyLayout, finalFinished, true);
this.Document.deiconifyLayout = undefined;
this._props.bringToFront?.(this.Document);
}
- }
+ });
public playAnnotation = () => {
const audioAnnoState = this.Document._audioAnnoState ?? AudioAnnoState.stopped;
@@ -1349,7 +1361,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
autoplay: true,
loop: false,
volume: 0.5,
- onend: action(() => { this.Document._audioAnnoState = AudioAnnoState.stopped; }), // prettier-ignore
+ onend: action(() => (this.Document._audioAnnoState = AudioAnnoState.stopped)),
});
this.Document._audioAnnoState = AudioAnnoState.playing;
break;
@@ -1405,14 +1417,10 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
tempDoc = view.Document;
MakeTemplate(tempDoc);
Doc.AddDocToList(Doc.UserDoc(), 'template_user', tempDoc);
- Doc.AddDocToList(DocListCast(Doc.MyTools.data)[1], 'data', makeUserTemplateButtonOrImage(tempDoc));
- tempDoc && Doc.AddDocToList(Cast(Doc.UserDoc().template_user, Doc, null), 'data', tempDoc);
+ Doc.AddDocToList(DocListCast(Doc.MyTools?.data)[1], 'data', makeUserTemplateButtonOrImage(tempDoc));
+ DocCast(Doc.UserDoc().template_user) && tempDoc && Doc.AddDocToList(DocCast(Doc.UserDoc().template_user)!, 'data', tempDoc);
} else {
- tempDoc = DocCast(view.Document[StrCast(view.Document.layout_fieldKey)]);
- if (!tempDoc) {
- tempDoc = view.Document;
- while (tempDoc && !Doc.isTemplateDoc(tempDoc)) tempDoc = DocCast(tempDoc.proto);
- }
+ tempDoc = DocCast(Doc.LayoutField(view.Document));
}
}
Doc.UserDoc().defaultTextLayout = tempDoc ? new PrefetchProxy(tempDoc) : undefined;
@@ -1434,10 +1442,10 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
if (this.Document.layout_fieldKey === 'layout_' + detailLayoutKeySuffix) this.switchViews(!!defaultLayout, defaultLayout, undefined, true);
else this.switchViews(true, detailLayoutKeySuffix, undefined, true);
};
- public switchViews = (custom: boolean, view: string, finished?: () => void, useExistingLayout = false) => {
+ public switchViews = action((custom: boolean, view: string, finished?: () => void, useExistingLayout = false) => {
const batch = UndoManager.StartBatch('switchView:' + view);
// shrink doc first..
- runInAction(() => { this._docViewInternal && (this._docViewInternal._animateScalingTo = 0.1); }); // prettier-ignore
+ this._docViewInternal && (this._docViewInternal._animateScalingTo = 0.1);
setTimeout(
action(() => {
if (useExistingLayout && custom && this.Document['layout_' + view]) {
@@ -1457,7 +1465,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
}),
Math.max(0, (this._docViewInternal?.animateScaleTime() ?? 0) - 10)
);
- };
+ });
/**
* @returns a hierarchy path through the nested DocumentViews that display this view. The last element of the path is this view.
*/
@@ -1510,7 +1518,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
ref={r => {
const val = r?.style.display !== 'none'; // if the outer overlay has been displayed, trigger the innner div to start it's opacity fade in transition
if (r && val !== this._enableHtmlOverlayTransitions) {
- setTimeout(action(() => { this._enableHtmlOverlayTransitions = val; })); // prettier-ignore
+ setTimeout(action(() => (this._enableHtmlOverlayTransitions = val)));
}
}}
style={{ display: !this._htmlOverlayText ? 'none' : undefined }}>
@@ -1537,12 +1545,8 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
<div
id={this.ViewGuid}
className="contentFittingDocumentView"
- onPointerEnter={action(() => {
- this._isHovering = true;
- })}
- onPointerLeave={action(() => {
- this._isHovering = false;
- })}>
+ onPointerEnter={action(() => (this._isHovering = true))} //
+ onPointerLeave={action(() => (this._isHovering = false))}>
{!this.Document || !this._props.PanelWidth() ? null : (
<div
className="contentFittingDocumentView-previewDoc"
@@ -1570,9 +1574,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
fitWidth={this.layout_fitWidthFunc}
ScreenToLocalTransform={this.screenToContentsTransform}
focus={this._props.focus || emptyFunction}
- ref={action((r: DocumentViewInternal | null) => {
- r && (this._docViewInternal = r);
- })}
+ ref={action((r: DocumentViewInternal | null) => r && (this._docViewInternal = r))}
/>
{this.htmlOverlay()}
{this.ComponentView?.infoUI?.()}
@@ -1616,7 +1618,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
if (dv && (!containingDoc || dv.containerViewPath?.().lastElement()?.Document === containingDoc)) {
DocumentView.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.Document));
} else {
- const container = DocCast(containingDoc ?? doc.embedContainer ?? Doc.BestEmbedding(doc));
+ const container = DocCast(containingDoc ?? doc.embedContainer ?? Doc.BestEmbedding(doc))!;
const showDoc = !Doc.IsSystem(container) && !cv ? container : doc;
options.toggleTarget = undefined;
DocumentView.showDocument(showDoc, options, () => DocumentView.showDocument(doc, { ...options, openLocation: undefined })).then(() => {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index ad6d93d43..de49f502f 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -80,7 +80,7 @@ export interface FieldViewSharedProps {
setTitleFocus?: () => void;
focus: FocusFuncType;
onClickScript?: () => ScriptField | undefined;
- onDoubleClickScript?: () => ScriptField;
+ onDoubleClickScript?: () => ScriptField | undefined;
onPointerDownScript?: () => ScriptField;
onPointerUpScript?: () => ScriptField;
onKey?: (e: KeyboardEvent, textBox: FormattedTextBox) => boolean | undefined;
diff --git a/src/client/views/nodes/IconTagBox.tsx b/src/client/views/nodes/IconTagBox.tsx
index e3924eca7..0bbd6a0d3 100644
--- a/src/client/views/nodes/IconTagBox.tsx
+++ b/src/client/views/nodes/IconTagBox.tsx
@@ -108,7 +108,7 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> {
</Tooltip>
)); // prettier-ignore
- const audioannos = StrListCast(this.View.Document[Doc.LayoutFieldKey(this.View.Document) + '_audioAnnotations_text']);
+ const audioannos = StrListCast(this.View.Document[Doc.LayoutDataKey(this.View.Document) + '_audioAnnotations_text']);
return !buttons.length && !audioannos.length ? null : (
<div className="card-button-container" style={{ fontSize: '50px' }}>
{audioannos.length ? this.renderAudioButtons(this.View, audioannos.lastElement()) : null}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 2b9a78596..0c475b7bb 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -216,7 +216,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
} else if (hitDropTarget(e.target as HTMLElement, this._regenerateIconRef.current)) {
this._regenerateLoading = true;
const drag = de.complete.docDragData.draggedDocuments.lastElement();
- const dragField = drag[Doc.LayoutFieldKey(drag)];
+ const dragField = drag[Doc.LayoutDataKey(drag)];
const descText = RTFCast(dragField)?.Text || StrCast(dragField) || RTFCast(drag.text)?.Text || StrCast(drag.text) || StrCast(this.Document.title);
const oldPrompt = StrCast(this.Document.ai_firefly_prompt, StrCast(this.Document.title));
const newPrompt = (text: string) => (oldPrompt ? `${oldPrompt} ~~~ ${text}` : text);
@@ -224,7 +224,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
added = false;
} else if (de.altKey || !this.dataDoc[this.fieldKey]) {
const layoutDoc = de.complete.docDragData?.draggedDocuments[0];
- const targetField = Doc.LayoutFieldKey(layoutDoc);
+ const targetField = Doc.LayoutDataKey(layoutDoc);
const targetDoc = layoutDoc[DocData];
if (targetDoc[targetField] instanceof ImageField) {
added = true;
@@ -264,7 +264,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
DocListCast(this.dataDoc[this.annotationKey]).forEach(doc => {
doc.x = (NumCast(doc.x) / oldnativeWidth) * newnativeWidth;
doc.y = (NumCast(doc.y) / oldnativeWidth) * newnativeWidth;
- if (!RTFCast(doc[Doc.LayoutFieldKey(doc)])) {
+ if (!RTFCast(doc[Doc.LayoutDataKey(doc)])) {
doc.width = (NumCast(doc.width) / oldnativeWidth) * newnativeWidth;
doc.height = (NumCast(doc.height) / oldnativeWidth) * newnativeWidth;
}
@@ -351,9 +351,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
event: () => {
Networking.PostToServer('/queryFireflyImageText', {
file: (file => {
- const ext = extname(file);
- return file.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
- })(ImageCast(this.Document[Doc.LayoutFieldKey(this.Document)])?.url.href),
+ const ext = file ? extname(file) : '';
+ return file?.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
+ })(ImageCast(this.Document[Doc.LayoutDataKey(this.Document)])?.url.href),
}).then(text => alert(text));
},
icon: 'expand-arrows-alt',
@@ -364,9 +364,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
Networking.PostToServer('/expandImage', {
prompt: 'sunny skies',
file: (file => {
- const ext = extname(file);
- return file.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
- })(ImageCast(this.Document[Doc.LayoutFieldKey(this.Document)])?.url.href),
+ const ext = file ? extname(file) : '';
+ return file?.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
+ })(ImageCast(this.Document[Doc.LayoutDataKey(this.Document)])?.url.href),
}).then(res => {
const info = res as Upload.ImageInformation;
const img = Docs.Create.ImageDocument(info.accessPaths.agnostic.client, { title: 'expand:' + this.Document.title });
@@ -510,7 +510,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
<div
className="imageBox-regenerateDropTarget"
ref={this._regenerateIconRef}
- onClick={() => DocumentView.showDocument(DocCast(this.Document.ai_firefly_generatedDocs), { openLocation: OpenWhere.addRight })}
+ onClick={() => DocCast(this.Document.ai_firefly_generatedDocs) && DocumentView.showDocument(DocCast(this.Document.ai_firefly_generatedDocs)!, { openLocation: OpenWhere.addRight })}
style={{
display: (this._props.isContentActive() && (SnappingManager.CanEmbed || this.Document.ai_firefly_generatedDocs)) || this._regenerateLoading ? 'block' : 'none',
transform: `scale(${this.uiBtnScaling})`,
@@ -532,7 +532,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const defaultUrl = new URL(ClientUtils.prepend(DefaultPath));
const altpaths =
alts
- ?.map(doc => (doc instanceof Doc ? (ImageCast(doc[Doc.LayoutFieldKey(doc)])?.url ?? defaultUrl) : defaultUrl))
+ ?.map(doc => (doc instanceof Doc ? (ImageCast(doc[Doc.LayoutDataKey(doc)])?.url ?? defaultUrl) : defaultUrl))
.filter(url => url)
.map(url => this.choosePath(url)) ?? []; // acc ess the primary layout data of the alternate documents
const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
@@ -664,7 +664,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const url = firstImg.pathname;
const imgField = new ImageField(url);
this._prevImgs.length === 0 &&
- this._prevImgs.push({ prompt: StrCast(this.dataDoc.ai_firefly_prompt), seed: this.dataDoc.ai_firefly_seed as number, href: this.paths.lastElement(), pathname: field.url.pathname });
+ this._prevImgs.push({ prompt: StrCast(this.dataDoc.ai_firefly_prompt), seed: this.dataDoc.ai_firefly_seed as number, href: this.paths.lastElement(), pathname: field?.url.pathname ?? '' });
this._prevImgs.unshift({ prompt: firstImg.prompt, seed: firstImg.seed, pathname: url });
this.dataDoc.ai_firefly_history = JSON.stringify(this._prevImgs);
this.dataDoc.ai_firefly_prompt = firstImg.prompt;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index be897b3f3..706607fe1 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -96,7 +96,7 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
const { script, type, onDelegate } = kvpScript;
const chooseDelegate = forceOnDelegate || onDelegate || keyIn.startsWith('_');
const key = chooseDelegate && keyIn.startsWith('$') ? keyIn.slice(1) : keyIn;
- const target = chooseDelegate ? doc : DocCast(doc.proto, doc);
+ const target = chooseDelegate ? doc : DocCast(doc.proto, doc)!;
let field: FieldType | undefined;
switch (type) {
case 'computed': field = new ComputedField(script); break; // prettier-ignore
@@ -261,15 +261,15 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
getFieldView = () => {
const rows = this.rows.filter(row => row.isChecked);
if (rows.length > 1) {
- const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${DocCast(this.Document).title}`, _chromeHidden: true });
+ const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${this.Document.title}`, _chromeHidden: true });
rows.forEach(row => {
- const field = this.createFieldView(DocCast(this.Document), row);
+ const field = this.createFieldView(this.Document, row);
field && Doc.AddDocToList(parent, 'data', field);
row.uncheck();
});
return parent;
}
- return rows.length ? this.createFieldView(DocCast(this.Document), rows.lastElement()) : undefined;
+ return rows.length ? this.createFieldView(this.Document, rows.lastElement()) : undefined;
};
createFieldView = (templateDoc: Doc, row: KeyValuePair) => {
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index c9e0aea5a..d8f968a40 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -61,7 +61,7 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
};
render() {
- let doc = this._props.keyName.startsWith('_') ? this._props.doc[DocLayout] : this._props.doc;
+ let doc: Doc | undefined = this._props.keyName.startsWith('_') ? this._props.doc[DocLayout] : this._props.doc;
const layoutField = doc !== this._props.doc;
const key = layoutField ? this._props.keyName.replace(/^_/, '') : this._props.keyName;
let protoCount = 0;
@@ -85,7 +85,7 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
style={hover}
className="keyValuePair-td-key-delete"
onClick={undoable(() => {
- delete (Object.keys(doc).indexOf(key) !== -1 ? doc : DocCast(this._props.doc.proto))[key];
+ doc && delete (Object.keys(doc).indexOf(key) !== -1 ? doc : DocCast(this._props.doc.proto)!)[key];
}, 'set key value')}>
X
</button>
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 36d260fb9..83c44c80f 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -210,7 +210,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._disposers.scroll = reaction(
() => this.layoutDoc.layout_scrollTop,
() => {
- if (!(ComputedField.WithoutComputed(() => FieldValue(this.Document[this.SidebarKey + '_panY'])) instanceof ComputedField)) {
+ if (!(ComputedField.DisableCompute(() => FieldValue(this.Document[this.SidebarKey + '_panY'])) instanceof ComputedField)) {
this.Document[this.SidebarKey + '_panY'] = ComputedField.MakeFunction('this.layout_scrollTop');
}
this.layoutDoc[this.SidebarKey + '_freeform_scale'] = 1;
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 53783e8a3..25c708da8 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -98,7 +98,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
});
screengrabber.overlayX = 70; // was -400
screengrabber.overlayY = 590; // was 0
- screengrabber['$' + Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
+ screengrabber['$' + Doc.LayoutDataKey(screengrabber) + '_trackScreen'] = true;
Doc.AddToMyOverlay(screengrabber); // just adds doc to overlay
DocumentView.addViewRenderedCb(screengrabber, docView => {
RecordingBox.screengrabber = docView.ComponentView as RecordingBox;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 999f9c1cd..4f02d68d6 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -284,7 +284,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
setupDictation = () => {
if (this.dataDoc[this.fieldKey + '_dictation']) return;
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);
+ const textField = Doc.LayoutDataKey(dictationText);
dictationText._layout_autoHeight = false;
const dictationTextProto = dictationText[DocData];
dictationTextProto[`${textField}_recordingSource`] = this.dataDoc;
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 8da422039..38a43e8d4 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -1,14 +1,12 @@
-/* eslint-disable react/button-has-type */
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import ResizeObserver from 'resize-observer-polyfill';
import { returnAlways, returnEmptyString } from '../../../ClientUtils';
-import { Doc } from '../../../fields/Doc';
+import { Doc, StrListCast } from '../../../fields/Doc';
import { List } from '../../../fields/List';
-import { listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
import { DocumentType } from '../../documents/DocumentTypes';
import { Docs } from '../../documents/Documents';
@@ -26,10 +24,8 @@ import './ScriptingBox.scss';
import * as ts from 'typescript';
import { FieldType } from '../../../fields/ObjectField';
-// eslint-disable-next-line @typescript-eslint/no-var-requires
const getCaretCoordinates = require('textarea-caret');
-// eslint-disable-next-line @typescript-eslint/no-var-requires
const ReactTextareaAutocomplete = require('@webscopeio/react-textarea-autocomplete').default;
@observer
@@ -105,7 +101,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
this.dataDoc[this.fieldKey + '-functionDescription'] = value;
}
@computed({ keepAlive: true }) get compileParams() {
- return Cast(this.dataDoc[this.fieldKey + '-params'], listSpec('string'), []);
+ return StrListCast(this.dataDoc[this.fieldKey + '-params']);
}
set compileParams(value) {
this.dataDoc[this.fieldKey + '-params'] = new List<string>(value);
@@ -426,7 +422,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
onChange={e => this.viewChanged(e, 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) => (
- // eslint-disable-next-line react/no-array-index-key
<option key={i} className="scriptingBox-viewOption" value={(typeof type === 'string' ? 'S' : typeof type === 'number' ? 'N' : 'B') + type}>
{' '}
{type.toString()}{' '}
@@ -680,7 +675,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
const definedParameters = !this.compileParams.length ? null : (
<div className="scriptingBox-plist" style={{ width: '30%' }}>
{this.compileParams.map((parameter, i) => (
- // eslint-disable-next-line react/no-array-index-key
<div key={i} className="scriptingBox-pborder" onKeyDown={e => e.key === 'Enter' && this._overlayDisposer?.()}>
<EditableView
display="block"
@@ -760,7 +754,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
{!this.compileParams.length || !this.paramsNames ? null : (
<div className="scriptingBox-plist">
{this.paramsNames.map((parameter: string, i: number) => (
- // eslint-disable-next-line react/no-array-index-key
<div key={i} className="scriptingBox-pborder" onKeyDown={e => e.key === 'Enter' && this._overlayDisposer?.()}>
<div className="scriptingBox-wrapper" style={{ maxHeight: '40px' }}>
<div className="scriptingBox-paramNames"> {`${parameter}:${this.paramsTypes[i]} = `} </div>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index bad4f5b13..5603786f0 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -6,7 +6,7 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import * as WebRequest from 'web-request';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivHeight, getWordAtPoint, lightOrDark, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll } from '../../../ClientUtils';
-import { Doc, DocListCast, Field, FieldType, Opt } from '../../../fields/Doc';
+import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { HtmlField } from '../../../fields/HtmlField';
import { InkTool } from '../../../fields/InkField';
@@ -645,8 +645,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
forward = (checkAvailable?: boolean) => {
- const future = Cast(this.dataDoc[this.fieldKey + '_future'], listSpec('string'), []);
- const history = Cast(this.dataDoc[this.fieldKey + '_history'], listSpec('string'), []);
+ const future = StrListCast(this.dataDoc[this.fieldKey + '_future']);
+ const history = StrListCast(this.dataDoc[this.fieldKey + '_history']);
if (checkAvailable) return future.length;
runInAction(() => {
if (future.length) {
@@ -672,23 +672,19 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
back = (checkAvailable?: boolean) => {
- const future = Cast(this.dataDoc[this.fieldKey + '_future'], listSpec('string'));
- const history = Cast(this.dataDoc[this.fieldKey + '_history'], listSpec('string'), []);
+ const future = StrListCast(this.dataDoc[this.fieldKey + '_future']);
+ const history = StrListCast(this.dataDoc[this.fieldKey + '_history']);
if (checkAvailable) return history.length;
runInAction(() => {
if (history.length) {
const curUrl = this._url;
- if (future === undefined) this.dataDoc[this.fieldKey + '_future'] = new List<string>([this._url]);
+ if (!future.length) this.dataDoc[this.fieldKey + '_future'] = new List<string>([this._url]);
else this.dataDoc[this.fieldKey + '_future'] = new List<string>([...future, this._url]);
this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
this._scrollHeight = 0;
if (this._webUrl === this._url) {
this._webUrl = curUrl;
- setTimeout(
- action(() => {
- this._webUrl = this._url;
- })
- );
+ setTimeout(action(() => (this._webUrl = this._url)));
} else {
this._webUrl = this._url;
}
diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
index b023b1de6..528bcd05a 100644
--- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
+++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
@@ -733,7 +733,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const x2 = parseFloat(values[2]) * Doc.NativeWidth(doc);
const y2 = parseFloat(values[3]) * Doc.NativeHeight(doc) + foundChunk.startPage * Doc.NativeHeight(doc);
- const annotationKey = '$' + Doc.LayoutFieldKey(doc) + '_annotations';
+ const annotationKey = '$' + Doc.LayoutDataKey(doc) + '_annotations';
const existingDoc = DocListCast(doc[annotationKey]).find(d => d.citation_id === citation.citation_id);
const highlightDoc = existingDoc ?? this.createImageCitationHighlight(x1, y1, x2, y2, citation, annotationKey, doc);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index cfb6dfa12..dc23a695d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -214,9 +214,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
- const rootDoc = Doc.isTemplateDoc(this._props.docViewPath().lastElement()?.Document) ? this.Document : this.rootDoc;
- if (!pinProps && this.EditorView?.state.selection.empty) return rootDoc;
- const anchor = Docs.Create.ConfigDocument({ title: StrCast(rootDoc.title), annotationOn: rootDoc });
+ if (!pinProps && this.EditorView?.state.selection.empty) return this.rootDoc;
+ const anchor = Docs.Create.ConfigDocument({ title: StrCast(this.rootDoc?.title), annotationOn: this.rootDoc });
this.addDocument(anchor);
this._finishingLink = true;
this.makeLinkAnchor(anchor, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation);
@@ -245,7 +244,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
anchor.followLinkAudio = true;
let stopFunc: () => void = emptyFunction;
target.$mediaState = mediaState.Recording;
- DictationManager.recordAudioAnnotation(target, Doc.LayoutFieldKey(target), stop => { stopFunc = stop }); // prettier-ignore
+ DictationManager.recordAudioAnnotation(target, Doc.LayoutDataKey(target), stop => { stopFunc = stop }); // prettier-ignore
const reactionDisposer = reaction(
() => target.mediaState,
@@ -427,8 +426,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const oldAutoLinks = Doc.Links(this.Document).filter(
link =>
((!Doc.isTemplateForField(this.Document) &&
- (!Doc.isTemplateForField(DocCast(link.link_anchor_1)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_1), this.Document)) &&
- (!Doc.isTemplateForField(DocCast(link.link_anchor_2)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_2), this.Document))) ||
+ ((DocCast(link.link_anchor_1) && !Doc.isTemplateForField(DocCast(link.link_anchor_1)!)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_1), this.Document)) &&
+ ((DocCast(link.link_anchor_2) && !Doc.isTemplateForField(DocCast(link.link_anchor_2)!)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_2), this.Document))) ||
(Doc.isTemplateForField(this.Document) && (link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document))) &&
link.link_relationship === LinkManager.AutoKeywords
); // prettier-ignore
@@ -450,18 +449,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
updateTitle = () => {
const title = StrCast(this.dataDoc.title, Cast(this.dataDoc.title, RichTextField, null)?.Text);
if (
- !this._props.dontRegisterView && // (this.Document.isTemplateForField === "text" || !this.Document.isTemplateForField) && // only update the title if the data document's data field is changing
+ !this._props.dontRegisterView && // only update the title if the data document's data field is changing
title.startsWith('-') &&
this.EditorView &&
!this.dataDoc.title_custom &&
- (Doc.LayoutFieldKey(this.Document) === this.fieldKey || this.fieldKey === 'text')
+ (Doc.LayoutDataKey(this.Document) === this.fieldKey || this.fieldKey === 'text')
) {
let node = this.EditorView.state.doc;
while (node.firstChild && node.firstChild.type.name !== 'text') node = node.firstChild;
const str = node.textContent;
const prefix = '-';
- const cfield = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc.title));
+ const cfield = ComputedField.DisableCompute(() => FieldValue(this.dataDoc.title));
if (!(cfield instanceof ComputedField)) {
this.dataDoc.title = (prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? '...' : '')).trim();
}
@@ -588,7 +587,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
const dragData = de.complete.docDragData;
if (dragData) {
- const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
+ const layoutProto = DocCast(this.layoutDoc.proto);
+ const dataDoc = layoutProto && Doc.IsDelegateField(layoutProto, this.fieldKey) ? layoutProto : this.dataDoc;
const effectiveAcl = GetEffectiveAcl(dataDoc);
const draggedDoc = dragData.droppedDocuments.lastElement();
let added: Opt<boolean>;
@@ -596,7 +596,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
if ([AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl) && !dragData.draggedDocuments.includes(this.Document)) {
// replace text contents when dragging with Alt
if (de.altKey) {
- const fieldKey = Doc.LayoutFieldKey(draggedDoc);
+ const fieldKey = Doc.LayoutDataKey(draggedDoc);
if (draggedDoc[fieldKey] instanceof RichTextField && !Doc.AreProtosEqual(draggedDoc, this.Document)) {
Doc.GetProto(this.dataDoc)[this.fieldKey] = Field.Copy(draggedDoc[fieldKey]);
}
@@ -1003,16 +1003,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const link = CreateLinkToActiveAudio(textanchorFunc, false).lastElement();
if (link) {
link.$isDictation = true;
- const audioanchor = Cast(link.link_anchor_2, Doc, null);
- const textanchor = Cast(link.link_anchor_1, Doc, null);
+ const audioanchor = DocCast(link.link_anchor_2);
+ const textanchor = DocCast(link.link_anchor_1);
if (audioanchor) {
audioanchor.backgroundColor = 'tan';
const audiotag = this.EditorView.state.schema.nodes.audiotag.create({
timeCode: NumCast(audioanchor._timecodeToShow),
audioId: audioanchor[Id],
- textId: textanchor[Id],
+ textId: textanchor?.[Id] ?? '',
});
- textanchor.$title = 'dictation:' + audiotag.attrs.timeCode;
+ textanchor && (textanchor.$title = 'dictation:' + audiotag.attrs.timeCode);
const tr = this.EditorView.state.tr.insert(this.EditorView.state.doc.content.size, audiotag);
const tr2 = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size));
this.EditorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size)));
@@ -1147,11 +1147,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
// if the scroll height has changed and we're in layout_autoHeight mode, then we need to update the textHeight component of the doc.
// 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);
+ resetNativeHeight = action((scrollHeight: number) => {
this.layoutDoc['_' + this.fieldKey + '_height'] = scrollHeight;
- if (nh) this.layoutDoc._nativeHeight = scrollHeight;
- };
+ if (!this.layoutDoc.isTemplateForField) this.layoutDoc._nativeHeight = scrollHeight;
+ });
addPlugin = (plugin: Plugin) => {
const editorView = this.EditorView;
@@ -1216,8 +1215,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const dataData = this.dataDoc[this.fieldKey];
const layoutData = Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? undefined : this.layoutDoc[this.fieldKey];
const dataTime = dataData ? (DateCast(this.dataDoc[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
- const layoutTime = layoutData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? (DateCast(DocCast(this.layoutDoc)[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
- const protoTime = protoData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? (DateCast(DocCast(this.dataDoc.proto)[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
+ const layoutTime = layoutData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? (DateCast(DocCast(this.layoutDoc)?.[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
+ const protoTime = protoData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? (DateCast(DocCast(this.dataDoc.proto)?.[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
const recentData = dataTime >= layoutTime ? (protoTime >= dataTime ? protoData : dataData) : layoutTime >= protoTime ? layoutData : protoData;
const whichData = recentData ?? (this.layoutDoc.isTemplateDoc ? layoutData : protoData) ?? protoData;
return !whichData ? undefined : { data: RTFCast(whichData), str: Field.toString(DocCast(whichData) ?? StrCast(whichData)) };
@@ -1313,7 +1312,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
// } catch (err) {
// console.log('Drop failed', err);
// }
- this.addDocument?.(DocCast(this.Document.image));
+ DocCast(this.Document.image) && this.addDocument?.(DocCast(this.Document.image)!);
}
//if (this.Document.image) this.addDocument?.(DocCast(this.Document.image));
@@ -1426,8 +1425,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const rtfField = Cast((!curText && this.layoutDoc[this.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
if (this.ProseRef) {
this.EditorView?.destroy();
+ const edState = () => {
+ try {
+ return rtfField?.Data ? EditorState.fromJSON(config, JSON.parse(rtfField.Data)) : EditorState.create(config);
+ } catch {
+ return EditorState.create(config);
+ }
+ };
this._editorView = new EditorView(this.ProseRef, {
- state: rtfField?.Data ? EditorState.fromJSON(config, JSON.parse(rtfField.Data)) : EditorState.create(config),
+ state: edState(),
handleScrollToSelection: this.scrollToSelection,
dispatchTransaction: this.dispatchTransaction,
nodeViews: FormattedTextBox._nodeViews(this),
@@ -1439,7 +1445,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
(this.EditorView as unknown as { scrollToSelection: unknown }).scrollToSelection = this.scrollToSelection;
const { state } = this._editorView;
if (!rtfField) {
- const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
+ const layoutProto = DocCast(this.layoutDoc.proto);
+ const dataDoc = layoutProto && Doc.IsDelegateField(layoutProto, this.fieldKey) ? layoutProto : this.dataDoc;
const startupText = Field.toString(dataDoc[fieldKey] as FieldType);
const textAlign = StrCast(this.dataDoc[this.fieldKey + '_align'], StrCast(Doc.UserDoc().textAlign)) || 'left';
if (textAlign !== 'left') {
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 4fa1fb13b..d31a58030 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -148,11 +148,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this._activeAlignment = this.getActiveAlignment();
this._activeFitBox = BoolCast(refDoc[refField + 'fitBox'], BoolCast(Doc.UserDoc().fitBox));
this._activeFontFamily = !activeFamilies.length
- ? StrCast(this.TextView?.Document._text_fontFamily, StrCast(this.dataDoc?.[Doc.LayoutFieldKey(this.dataDoc) + '_fontFamily'], refVal('fontFamily', 'Arial')))
+ ? StrCast(this.TextView?.Document._text_fontFamily, StrCast(this.dataDoc?.[Doc.LayoutDataKey(this.dataDoc) + '_fontFamily'], refVal('fontFamily', 'Arial')))
: activeFamilies.length === 1
? String(activeFamilies[0])
: 'various';
- this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, StrCast(this.dataDoc?.[Doc.LayoutFieldKey(this.dataDoc) + '_fontSize'], refVal('fontSize', '10px'))) : activeSizes[0];
+ this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, StrCast(this.dataDoc?.[Doc.LayoutDataKey(this.dataDoc) + '_fontSize'], refVal('fontSize', '10px'))) : activeSizes[0];
this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, refVal('fontColor', 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...';
this._activeHighlightColor = !activeHighlights.length ? '' : activeHighlights.length > 0 ? String(activeHighlights[0]) : '...';
@@ -382,7 +382,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
});
}
} else if (this.dataDoc) {
- this.dataDoc[`${Doc.LayoutFieldKey(this.dataDoc)}_${fontField}`] = value;
+ this.dataDoc[`${Doc.LayoutDataKey(this.dataDoc)}_${fontField}`] = value;
this.updateMenu(undefined, undefined, undefined, this.dataDoc);
} else {
Doc.UserDoc()[fontField] = value;
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 14ea911b1..26ccf6931 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -108,25 +108,23 @@ export class RichTextRules {
new InputRule(/>>$/, (state, match, start, end) => {
const numInlines = NumCast(this.Document.$inlineTextCount);
this.Document.$inlineTextCount = numInlines + 1;
- const inlineFieldKey = '$inline' + numInlines; // which field on the text document this annotation will write to
- const inlineLayoutKey = '$layout_' + inlineFieldKey; // the field holding the layout string that will render the inline annotation
+ const inlineFieldKey = 'inline' + numInlines; // which field on the text document this annotation will write to
+ const inlineLayoutKey = 'layout_' + inlineFieldKey; // the field holding the layout string that will render the inline annotation
const textDocInline = Docs.Create.TextDocument('', {
- layout_fieldKey: inlineLayoutKey,
_width: 75,
_height: 35,
- annotationOn: this.Document[DocData],
_layout_fitWidth: true,
_layout_autoHeight: true,
- text_fontSize: '9px',
- title: 'inline comment',
});
+ textDocInline.layout_fieldKey = inlineLayoutKey;
+ textDocInline.annotationOn = this.Document[DocData];
textDocInline.title = inlineFieldKey; // give the annotation its own title
textDocInline.title_custom = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
- textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
+ //textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
textDocInline.isDataDoc = true;
textDocInline.proto = this.Document[DocData]; // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]]
- this.Document[inlineLayoutKey] = FormattedTextBox.LayoutString(inlineFieldKey); // create a layout string for the layout key that will render the annotation text
- this.Document[inlineFieldKey] = ''; // set a default value for the annotation
+ this.Document['$' + inlineLayoutKey] = FormattedTextBox.LayoutString(inlineFieldKey); // create a layout string for the layout key that will render the annotation text
+ this.Document['$' + inlineFieldKey] = ''; // set a default value for the annotation
const node = state.doc.resolve(start).nodeAfter;
const newNode = schema.nodes.dashComment.create({ docId: textDocInline[Id], reflow: true });
const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: 'dashDoc', docId: textDocInline[Id], float: 'right' });
@@ -287,7 +285,8 @@ export class RichTextRules {
editor.dispatch(estate.tr.setSelection(new TextSelection(estate.doc.resolve(start), estate.doc.resolve(end - prefixLength))));
}
- DocUtils.MakeLink(this.TextBox.getAnchor(true), target, { link_relationship: 'portal to:portal from' });
+ const tanchor = this.TextBox.getAnchor(true);
+ tanchor && DocUtils.MakeLink(tanchor, target, { link_relationship: 'portal to:portal from' });
const teditor = this.TextBox.EditorView;
if (teditor && selection) {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 23155ebf3..11f35b8ef 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -149,7 +149,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
setIsLoading = action((input?: boolean) => { this._isLoading = !!input; }); // prettier-ignore
setShowAIGalleryVisibilty = action((visible: boolean) => { this._showAIGallery = visible; }); // prettier-ignore
setBezierControlPoints = action((newPoints: { p1: number[]; p2: number[] }) => {
- this.setEaseFunc(this.activeItem, `cubic-bezier(${newPoints.p1[0]}, ${newPoints.p1[1]}, ${newPoints.p2[0]}, ${newPoints.p2[1]})`);
+ this.activeItem && this.setEaseFunc(this.activeItem, `cubic-bezier(${newPoints.p1[0]}, ${newPoints.p1[1]}, ${newPoints.p2[0]}, ${newPoints.p2[1]})`);
});
@computed get showEaseFunctions() {
@@ -158,7 +158,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed
get currCPoints() {
- return EaseFuncToPoints(this.activeItem.presentation_easeFunc ? StrCast(this.activeItem.presentation_easeFunc) : 'ease');
+ return EaseFuncToPoints(this.activeItem?.presentation_easeFunc ? StrCast(this.activeItem.presentation_easeFunc) : 'ease');
}
@computed
@@ -175,7 +175,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return DocListCast(this.Document[this.presFieldKey]);
}
@computed get tagDocs() {
- return this.childDocs.map(doc => Cast(doc.presentation_targetDoc, Doc, null));
+ return this.childDocs.map(doc => DocCast(doc.presentation_targetDoc)!).filter(doc => doc);
}
@computed get itemIndex() {
return NumCast(this.Document._itemIndex);
@@ -187,11 +187,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return DocCast(this.activeItem?.presentation_targetDoc);
}
public static targetRenderedDoc = (doc: Doc) => {
- const targetDoc = Cast(doc?.presentation_targetDoc, Doc, null);
+ const targetDoc = DocCast(doc?.presentation_targetDoc);
return targetDoc?.layout_unrendered ? DocCast(targetDoc.annotationOn) : targetDoc;
};
@computed get scrollable() {
- if ([DocumentType.PDF, DocumentType.WEB, DocumentType.RTF].includes(this.targetDoc.type as DocumentType) || this.targetDoc._type_collection === CollectionViewType.Stacking) return true;
+ if ([DocumentType.PDF, DocumentType.WEB, DocumentType.RTF].includes(this.targetDoc?.type as DocumentType) || this.targetDoc?._type_collection === CollectionViewType.Stacking) return true;
return false;
}
@computed get selectedDocumentView() {
@@ -273,8 +273,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
stopTempMedia = (targetDocField: FieldResult) => {
- const targetDoc = DocCast(DocCast(targetDocField).annotationOn) ?? DocCast(targetDocField);
- if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as DocumentType)) {
+ const targetDoc = DocCast(DocCast(targetDocField)?.annotationOn) ?? DocCast(targetDocField);
+ if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc?.type as DocumentType)) {
const targMedia = DocumentView.getDocumentView(targetDoc);
targMedia?.ComponentView?.Pause?.();
}
@@ -295,13 +295,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.setIsLoading(true);
const slideDefaults: { [key: string]: FieldResult } = { presentation_transition: 500, config_zoom: 1 };
const currSlideProperties = gptSlideProperties.reduce(
- (prev, key) => { prev[key] = Field.toString(this.activeItem[key]) ?? prev[key]; return prev; },
+ (prev, key) => { prev[key] = Field.toString(this.activeItem?.[key]) ?? prev[key]; return prev; },
slideDefaults); // prettier-ignore
gptTrailSlideCustomization(input, JSON.stringify(currSlideProperties))
.then(res =>
(Object.entries(JSON.parse(res)) as string[][]).forEach(([key, val]) => {
- this.activeItem[key] = (+val).toString() === val ? +val : (val ?? this.activeItem[key]);
+ this.activeItem && (this.activeItem[key] = (+val).toString() === val ? +val : (val ?? this.activeItem[key]));
})
)
.catch(e => console.error(e))
@@ -325,7 +325,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const serial = nextSelected + 1 < this.childDocs.length && NumCast(this.childDocs[nextSelected + 1].presentation_groupWithUp) > 1;
if (serial) {
this.gotoDocument(nextSelected, this.activeItem, true, async () => {
- const waitTime = NumCast(this.activeItem.presentation_duration);
+ const waitTime = NumCast(this.activeItem?.presentation_duration);
await new Promise<void>(res => {
setTimeout(res, Math.max(0, waitTime));
});
@@ -346,7 +346,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
progressivizedItems = (doc: Doc) => {
const targetList = PresBox.targetRenderedDoc(doc);
if (doc.presentation_indexed !== undefined && targetList) {
- const listItems = (Cast(targetList[Doc.LayoutFieldKey(targetList)], listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[]) ?? DocListCast(targetList[Doc.LayoutFieldKey(targetList) + '_annotations']);
+ const listItems = (Cast(targetList[Doc.LayoutDataKey(targetList)], listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[]) ?? DocListCast(targetList[Doc.LayoutDataKey(targetList) + '_annotations']);
return listItems.filter(ldoc => !ldoc.layout_unrendered);
}
return undefined;
@@ -364,7 +364,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
next = () => {
const progressiveReveal = (first: boolean) => {
const presIndexed = Cast(this.activeItem?.presentation_indexed, 'number', null);
- if (presIndexed !== undefined) {
+ if (presIndexed !== undefined && this.activeItem) {
const listItems = this.progressivizedItems(this.activeItem);
const listItemDoc = listItems?.[presIndexed];
if (listItems && listItemDoc) {
@@ -395,7 +395,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (this.childDocs[this.itemIndex + 1] !== undefined) {
// Case 1: No more frames in current doc and next slide is defined, therefore move to next slide
const slides = DocListCast(this.Document[StrCast(this.presFieldKey, 'data')]);
- const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d)))) : this.itemIndex;
+ const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d) ?? d))) : this.itemIndex;
// before moving onto next slide, run the subroutines :)
const currentDoc = this.childDocs[this.itemIndex];
@@ -422,7 +422,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const { activeItem } = this;
let prevSelected = this.itemIndex;
// Functionality for group with up
- let didZoom = activeItem.presentation_movement;
+ let didZoom = activeItem?.presentation_movement;
for (; prevSelected > 0 && this.childDocs[Math.max(0, prevSelected - 1)].presentation_groupWithUp; prevSelected--) {
didZoom = didZoom === 'none' ? this.childDocs[prevSelected].presentation_movement : didZoom;
}
@@ -452,7 +452,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.stopTempMedia(from.presentation_targetDoc);
}
// If next slide is audio / video 'Play automatically' then the next slide should be played
- if (this.layoutDoc.presentation_status !== PresStatus.Edit && (this.targetDoc.type === DocumentType.AUDIO || this.targetDoc.type === DocumentType.VID) && this.activeItem.presentation_mediaStart === 'auto') {
+ if (this.layoutDoc.presentation_status !== PresStatus.Edit && (this.targetDoc?.type === DocumentType.AUDIO || this.targetDoc?.type === DocumentType.VID) && this.activeItem?.presentation_mediaStart === 'auto') {
this.startTempMedia(this.targetDoc, this.activeItem);
}
if (!group) this.clearSelectedArray();
@@ -516,8 +516,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const activeFrame = activeItem.config_activeFrame ?? activeItem.config_currentFrame;
if (activeFrame !== undefined) {
const frameTime = NumCast(activeItem.presentation_transition, 500);
- const acontext = activeItem.config_activeFrame !== undefined ? DocCast(DocCast(activeItem.presentation_targetDoc).embedContainer) : DocCast(activeItem.presentation_targetDoc);
- const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext).annotationOn) : acontext;
+ const acontext = activeItem.config_activeFrame !== undefined ? DocCast(DocCast(activeItem?.presentation_targetDoc)?.embedContainer) : DocCast(activeItem.presentation_targetDoc);
+ const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext)?.annotationOn) : acontext;
if (context) {
const ffview = CollectionFreeFormView.from(DocumentView.getFirstDocumentView(context));
if (ffview?.childDocs) {
@@ -528,7 +528,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
if ((pinDataTypes?.dataview && activeItem.config_data !== undefined) || (!pinDataTypes && activeItem.config_data !== undefined)) {
bestTarget._dataTransition = `all ${transTime}ms`;
- const fkey = Doc.LayoutFieldKey(bestTarget);
+ const fkey = Doc.LayoutDataKey(bestTarget);
const setData = bestTargetView?.ComponentView?.setData;
if (setData) setData(activeItem.config_data);
else {
@@ -553,7 +553,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
}
if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.config_clipWidth !== undefined)) {
- const fkey = '_' + Doc.LayoutFieldKey(bestTarget);
+ const fkey = '_' + Doc.LayoutDataKey(bestTarget);
if (bestTarget[fkey + '_clipWidth'] !== activeItem.config_clipWidth) {
bestTarget[fkey + '_clipWidth'] = activeItem.config_clipWidth;
changed = true;
@@ -655,7 +655,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
}
if (pinDataTypes?.dataannos || (!pinDataTypes && activeItem.config_annotations !== undefined)) {
- const fkey = Doc.LayoutFieldKey(bestTarget);
+ const fkey = Doc.LayoutDataKey(bestTarget);
const oldItems = DocListCast(bestTarget[fkey + '_annotations']).filter(doc => doc.layout_unrendered);
const newItems = DocListCast(activeItem.config_annotations).map(doc => {
doc.hidden = false;
@@ -672,7 +672,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
if (pinDataTypes?.poslayoutview || (!pinDataTypes && activeItem.config_pinLayoutData !== undefined)) {
changed = true;
- const layoutField = Doc.LayoutFieldKey(bestTarget);
+ const layoutField = Doc.LayoutDataKey(bestTarget);
const transitioned = new Set<Doc>();
StrListCast(activeItem.config_pinLayoutData)
.map(str => JSON.parse(str) as { id: string; x: number; y: number; back: string; fill: string; w: number; h: number; data: string; text: string })
@@ -740,7 +740,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const { activeItem, targetDoc } = this;
const finished = (options: FocusViewOptions) => {
afterNav?.(options);
- targetDoc[Animation] = undefined;
+ targetDoc && (targetDoc[Animation] = undefined);
};
const selViewCache = Array.from(this.selectedArray);
const dragViewCache = Array.from(this._dragArray);
@@ -756,7 +756,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
finished(options);
});
- PresBox.NavigateToTarget(targetDoc, activeItem, resetSelection);
+ targetDoc && activeItem && PresBox.NavigateToTarget(targetDoc, activeItem, resetSelection);
};
static NavigateToTarget(targetDoc: Doc, activeItem: Doc, finished?: (options: FocusViewOptions) => void) {
@@ -806,42 +806,44 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@action
doHideBeforeAfter = () => {
this.childDocs.forEach((doc, index) => {
- const curDoc = Cast(doc, Doc, null);
- const tagDoc = PresBox.targetRenderedDoc(curDoc);
- const itemIndexes = this.getAllIndexes(this.tagDocs, curDoc);
- let opacity = index === this.itemIndex ? 1 : undefined;
- if (curDoc.presentation_hide) {
- if (index !== this.itemIndex) {
- opacity = 1;
+ const curDoc = DocCast(doc);
+ if (curDoc) {
+ const tagDoc = PresBox.targetRenderedDoc(curDoc) ?? curDoc;
+ const itemIndexes = this.getAllIndexes(this.tagDocs, curDoc);
+ let opacity = index === this.itemIndex ? 1 : undefined;
+ if (curDoc.presentation_hide) {
+ if (index !== this.itemIndex) {
+ opacity = 1;
+ }
}
- }
- const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex) ?? itemIndexes.slice().reverse().lastElement();
- if (curDoc.presentation_hideBefore && index === hidingIndBef) {
- if (index > this.itemIndex) {
- opacity = 0;
- } else if (index === this.itemIndex || !curDoc.presentation_hideAfter) {
- opacity = 1;
+ const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex) ?? itemIndexes.slice().reverse().lastElement();
+ if (curDoc.presentation_hideBefore && index === hidingIndBef) {
+ if (index > this.itemIndex) {
+ opacity = 0;
+ } else if (index === this.itemIndex || !curDoc.presentation_hideAfter) {
+ opacity = 1;
+ }
}
- }
- const hidingIndAft =
- itemIndexes
- .slice()
- .reverse()
- .find(item => item <= this.itemIndex) ?? itemIndexes.lastElement();
- if (curDoc.presentation_hideAfter && index === hidingIndAft) {
- if (index < this.itemIndex) {
- opacity = 0;
- } else if (index === this.itemIndex || !curDoc.presentation_hideBefore) {
- opacity = 1;
+ const hidingIndAft =
+ itemIndexes
+ .slice()
+ .reverse()
+ .find(item => item <= this.itemIndex) ?? itemIndexes.lastElement();
+ if (curDoc.presentation_hideAfter && index === hidingIndAft) {
+ if (index < this.itemIndex) {
+ opacity = 0;
+ } else if (index === this.itemIndex || !curDoc.presentation_hideBefore) {
+ opacity = 1;
+ }
}
- }
- const hidingInd = itemIndexes.find(item => item === this.itemIndex);
- if (curDoc.presentation_hide && index === hidingInd) {
- if (index === this.itemIndex) {
- opacity = 0;
+ const hidingInd = itemIndexes.find(item => item === this.itemIndex);
+ if (curDoc.presentation_hide && index === hidingInd) {
+ if (index === this.itemIndex) {
+ opacity = 0;
+ }
}
+ opacity !== undefined && (tagDoc.opacity = opacity === 1 ? undefined : opacity);
}
- opacity !== undefined && (tagDoc.opacity = opacity === 1 ? undefined : opacity);
});
};
@@ -940,7 +942,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
initializePresState = (startIndex: number) => {
this.childDocs.forEach((doc, index) => {
- const tagDoc = PresBox.targetRenderedDoc(doc);
+ const tagDoc = PresBox.targetRenderedDoc(doc) ?? doc;
if (doc.presentation_hideBefore && index > startIndex) tagDoc.opacity = 0;
if (doc.presentation_hideAfter && index < startIndex) tagDoc.opacity = 0;
if (doc.presentation_indexed !== undefined && index >= startIndex) {
@@ -969,7 +971,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.layoutDoc.presentation_status = PresStatus.Autoplay;
this.initializePresState(startIndex);
const func = () => {
- const delay = NumCast(this.activeItem.presentation_duration, this.activeItem.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem.presentation_transition);
+ const delay = NumCast(this.activeItem?.presentation_duration, this.activeItem?.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem?.presentation_transition);
this._presTimer = setTimeout(() => {
if (this.next() === false) this.layoutDoc.presentation_status = this._exitTrail?.() ?? PresStatus.Manual;
this.layoutDoc.presentation_status === PresStatus.Autoplay && func();
@@ -1060,7 +1062,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return !results.some(r => !r);
};
- childLayoutTemplate = () => Docs.Create.PresElementBoxDocument();
+ childLayoutTemplate = () => Docs.Create.PresSlideDocument();
removeDocument = (doc: Doc | Doc[]) =>
!toList(doc)
.map(d => Doc.RemoveDocFromList(this.Document, this.fieldKey, d))
@@ -1077,13 +1079,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
*/
@computed get listOfSelected() {
return Array.from(this.selectedArray).map((doc, index) => {
- const curDoc = Cast(doc, Doc, null);
+ const curDoc = DocCast(doc);
+ if (!curDoc) return null;
const tagDoc = Cast(curDoc.presentation_targetDoc, Doc, null);
if (curDoc && curDoc === this.activeItem)
return (
<div key={doc[Id]} className="selectedList-items">
<b>
- {index + 1}. {StrCast(curDoc.title)})
+ {index + 1}. {StrCast(curDoc.title)}
</b>
</div>
);
@@ -1277,7 +1280,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const presCollection = collection;
const dv = DocumentView.getDocumentView(presCollection);
this.childDocs.forEach((doc, index) => {
- const tagDoc = PresBox.targetRenderedDoc(doc);
+ const tagDoc = PresBox.targetRenderedDoc(doc) ?? doc;
const srcContext = Cast(tagDoc.embedContainer, Doc, null);
const labelCreator = (top: number, left: number, edge: number, fontSize: number) => (
<div className="pathOrder" key={tagDoc.id + 'pres' + index} style={{ top, left, width: edge, height: edge, fontSize }} onClick={() => this.selectElement(doc)}>
@@ -1526,17 +1529,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
// Applies the slide transiiton settings to all docs in the array
@undoBatch
applyTo = (array: Doc[]) => {
- this.updateMovement(this.activeItem.presentation_movement as PresMovement, true);
- this.updateEffect(this.activeItem.presentation_effect as PresEffect, false, true);
- this.updateEffect(this.activeItem.presBulletEffect as PresEffect, true, true);
- this.updateEffectDirection(this.activeItem.presentation_effectDirection as PresEffectDirection, true);
- const { presentation_transition: pt, presentation_duration: pd, presentation_hideBefore: ph, presentation_hideAfter: pa } = this.activeItem;
- array.forEach(curDoc => {
- curDoc.presentation_transition = pt;
- curDoc.presentation_duration = pd;
- curDoc.presentation_hideBefore = ph;
- curDoc.presentation_hideAfter = pa;
- });
+ if (this.activeItem) {
+ this.updateMovement(this.activeItem.presentation_movement as PresMovement, true);
+ this.updateEffect(this.activeItem.presentation_effect as PresEffect, false, true);
+ this.updateEffect(this.activeItem.presBulletEffect as PresEffect, true, true);
+ this.updateEffectDirection(this.activeItem.presentation_effectDirection as PresEffectDirection, true);
+ const { presentation_transition: pt, presentation_duration: pd, presentation_hideBefore: ph, presentation_hideAfter: pa } = this.activeItem;
+ array.forEach(curDoc => {
+ curDoc.presentation_transition = pt;
+ curDoc.presentation_duration = pd;
+ curDoc.presentation_hideBefore = ph;
+ curDoc.presentation_hideAfter = pa;
+ });
+ }
};
@computed get visibilityDurationDropdown() {
@@ -1673,15 +1678,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
onClick={() => {
activeItem.presentation_indexed = activeItem.presentation_indexed === undefined ? 0 : undefined;
activeItem.presentation_hideBefore = activeItem.presentation_indexed !== undefined;
- const tagDoc = PresBox.targetRenderedDoc(this.activeItem);
+ const tagDoc = PresBox.targetRenderedDoc(activeItem) ?? activeItem;
const type = DocCast(tagDoc?.annotationOn)?.type ?? tagDoc.type;
activeItem.presentation_indexedStart = type === DocumentType.COL ? 1 : 0;
// a progressivized slide doesn't have sub-slides, but rather iterates over the data list of the target being progressivized.
// to avoid creating a new slide to correspond to each of the target's data list, we create a computedField to refernce the target's data list.
- let dataField = Doc.LayoutFieldKey(tagDoc);
+ let dataField = Doc.LayoutDataKey(tagDoc);
if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField += '_annotations';
- if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc.annotationOn?.["${dataField}"]`);
+ if (DocCast(activeItem.presentation_targetDoc)?.annotationOn) activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc.annotationOn?.["${dataField}"]`);
else activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc?.["${dataField}"]`);
}}>
Enable
@@ -1797,12 +1802,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
onClick={() => {
this.updateEffect(elem.effect, false);
this.updateEffectDirection(elem.direction);
- this.updateEffectTiming(this.activeItem, {
- type: SpringType.CUSTOM,
- stiffness: elem.stiffness,
- damping: elem.damping,
- mass: elem.mass,
- });
+ this.activeItem &&
+ this.updateEffectTiming(this.activeItem, {
+ type: SpringType.CUSTOM,
+ stiffness: elem.stiffness,
+ damping: elem.damping,
+ mass: elem.mass,
+ });
}}>
<SlideEffect dir={elem.direction} presEffect={elem.effect} springSettings={elem} infinite>
<div className="presBox-effect-demo-box" style={{ backgroundColor: springPreviewColors[i] }} />
@@ -1947,8 +1953,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
formLabelPlacement="left"
closeOnSelect
items={easeItems}
- selectedVal={this.activeItem.presentation_easeFunc ? (StrCast(this.activeItem.presentation_easeFunc).startsWith('cubic') ? 'custom' : StrCast(this.activeItem.presentation_easeFunc)) : 'ease'}
- setSelectedVal={val => typeof val === 'string' && this.setEaseFunc(this.activeItem, val !== 'custom' ? val : TIMING_DEFAULT_MAPPINGS.ease)}
+ selectedVal={this.activeItem?.presentation_easeFunc ? (StrCast(this.activeItem.presentation_easeFunc).startsWith('cubic') ? 'custom' : StrCast(this.activeItem.presentation_easeFunc)) : 'ease'}
+ setSelectedVal={val => typeof val === 'string' && this.activeItem && this.setEaseFunc(this.activeItem, val !== 'custom' ? val : TIMING_DEFAULT_MAPPINGS.ease)}
dropdownType={DropdownType.SELECT}
type={Type.TERT}
/>
@@ -2145,9 +2151,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get mediaOptionsDropdown() {
const { activeItem } = this;
if (activeItem && this.targetDoc) {
- const renderTarget = PresBox.targetRenderedDoc(this.activeItem);
+ const renderTarget = PresBox.targetRenderedDoc(this.activeItem ?? this.targetDoc) ?? this.targetDoc;
const clipStart = NumCast(renderTarget.clipStart);
- const clipEnd = NumCast(renderTarget.clipEnd, clipStart + NumCast(renderTarget[Doc.LayoutFieldKey(renderTarget) + '_duration']));
+ const clipEnd = NumCast(renderTarget.clipEnd, clipStart + NumCast(renderTarget[Doc.LayoutDataKey(renderTarget) + '_duration']));
const configClipEnd = NumCast(activeItem.config_clipEnd) < NumCast(activeItem.config_clipStart) ? clipEnd - clipStart : NumCast(activeItem.config_clipEnd);
return (
<div className="presBox-ribbon" onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
@@ -2699,7 +2705,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const presEnd =
!this.layoutDoc.presLoop &&
this.itemIndex === this.childDocs.length - 1 &&
- (this.activeItem.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
+ (this.activeItem?.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
const inOverlay = Doc.IsInMyOverlay(this.Document);
// Case 1: There are still other frames and should go through all frames before going to next slide
@@ -2892,7 +2898,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const presEnd =
!this.layoutDoc.presLoop &&
this.itemIndex === this.childDocs.length - 1 &&
- (this.activeItem.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
+ (this.activeItem?.presentation_indexed === undefined || NumCast(this.activeItem.presentation_indexed) === (this.progressivizedItems(this.activeItem)?.length ?? 0));
const presStart = !this.layoutDoc.presLoop && this.itemIndex === 0;
return this._props.addDocTab === returnFalse ? ( // bcz: hack!! - addDocTab === returnFalse only when this is being rendered by the OverlayView which means the doc is a mini player
<div
@@ -2984,16 +2990,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
/>
) : null}
</div>
- {/* {
- // if the document type is a presentation, then the collection stacking view has a "+ new slide" button at the bottom of the stack
- <Tooltip title={<div className="dash-tooltip">{'Click on document to pin to presentaiton or make a marquee selection to pin your desired view'}</div>}>
- <button className="add-slide-button" onPointerDown={this.startMarqueeCreateSlide}>
- + NEW SLIDE
- </button>
- </Tooltip>
- } */}
</div>
- {/* presbox chatbox */}
{this._chatActive && <div className="presBox-chatbox" />}
</div>
);
diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresSlideBox.scss
index 9ac2b5a94..9ac2b5a94 100644
--- a/src/client/views/nodes/trails/PresElementBox.scss
+++ b/src/client/views/nodes/trails/PresSlideBox.scss
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresSlideBox.tsx
index 7e0375275..3dbb3da88 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresSlideBox.tsx
@@ -7,7 +7,7 @@ import { returnFalse, returnTrue, setupMoveUpEvents } from '../../../../ClientUt
import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
-import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
import { emptyFunction } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
@@ -25,16 +25,16 @@ import { returnEmptyDocViewList } from '../../StyleProvider';
import { DocumentView } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
import { PresBox } from './PresBox';
-import './PresElementBox.scss';
+import './PresSlideBox.scss';
import { PresMovement } from './PresEnums';
/**
* This class models the view a document added to presentation will have in the presentation.
* It involves some functionality for its buttons and options.
*/
@observer
-export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
+export class PresSlideBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) {
- return FieldView.LayoutString(PresElementBox, fieldKey);
+ return FieldView.LayoutString(PresSlideBox, fieldKey);
}
private _itemRef: React.RefObject<HTMLDivElement> = React.createRef();
private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
@@ -56,7 +56,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
.containerViewPath?.()
.slice()
.reverse()
- .find(dv => dv?.ComponentView instanceof PresBox)?.ComponentView as PresBox;
+ .find(dv => dv?.ComponentView instanceof PresBox)?.ComponentView as Opt<PresBox>;
}
// the presentation view document that renders this slide
@@ -72,7 +72,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
// this is the document in the workspaces that is targeted by the slide
@computed get targetDoc() {
- return Cast(this.slideDoc.presentation_targetDoc, Doc, null) || this.slideDoc;
+ return DocCast(this.slideDoc.presentation_targetDoc, this.slideDoc)!;
}
// computes index of this presentation slide in the presBox list
@@ -118,7 +118,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
return !this.slideDoc.presentation_expandInlineButton || !this.targetDoc ? null : (
<div className="presItem-embedded" style={{ height: this.embedHeight(), width: '50%' }}>
<DocumentView
- Document={PresBox.targetRenderedDoc(this.slideDoc)}
+ Document={PresBox.targetRenderedDoc(this.slideDoc) ?? this.slideDoc}
PanelWidth={this.embedWidth}
PanelHeight={this.embedHeight}
isContentActive={this._props.isContentActive}
@@ -349,7 +349,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
/// remove all videos that have been recorded from overlay (leave videso that are being recorded to avoid losing data)
static removeEveryExistingRecordingInOverlay = () => {
- Doc.MyOverlayDocs.filter(doc => doc.slides !== null && PresElementBox.videoIsRecorded(DocCast(doc.slides))) //
+ Doc.MyOverlayDocs.filter(doc => doc.slides !== null && PresSlideBox.videoIsRecorded(DocCast(doc.slides))) //
.forEach(Doc.RemFromMyOverlay);
};
@@ -364,9 +364,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
showRecording = undoable(
action((activeItem: Doc, iconClick: boolean = false) => {
// remove the overlays on switch *IF* not opened from the specific icon
- if (!iconClick) PresElementBox.removeEveryExistingRecordingInOverlay();
+ if (!iconClick) PresSlideBox.removeEveryExistingRecordingInOverlay();
- activeItem.recording && Doc.AddToMyOverlay(DocCast(activeItem.recording));
+ DocCast(activeItem.recording) && Doc.AddToMyOverlay(DocCast(activeItem.recording)!);
}),
'show video recording'
);
@@ -374,7 +374,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
startRecording = undoable(
action((e: React.MouseEvent, activeItem: Doc) => {
e.stopPropagation();
- if (PresElementBox.videoIsRecorded(activeItem)) {
+ if (PresSlideBox.videoIsRecorded(activeItem)) {
// if we already have an existing recording
this.showRecording(activeItem, true);
// // if we already have an existing recording
@@ -383,7 +383,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
// we dont have any recording
// Remove every recording that already exists in overlay view
// this is a design decision to clear to focus in on the recoding mode
- PresElementBox.removeEveryExistingRecordingInOverlay();
+ PresSlideBox.removeEveryExistingRecordingInOverlay();
// create and add a recording to the slide
// make recording box appear in the bottom right corner of the screen
@@ -413,7 +413,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
TreeView.ToggleChildrenRun.get(this.slideDoc)?.();
// call this.slideDoc.recurChildren() to get all the children
- // if (iconClick) PresElementBox.showVideo = false;
+ // if (iconClick) PresSlideBox.showVideo = false;
};
@computed
@@ -452,7 +452,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
</Tooltip>
);
items.push(
- <Tooltip key="slash" title={<div className="dash-tooltip">{this.videoRecordingIsInOverlay ? 'Hide Recording' : `${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}</div>}>
+ <Tooltip key="slash" title={<div className="dash-tooltip">{this.videoRecordingIsInOverlay ? 'Hide Recording' : `${PresSlideBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}</div>}>
<div className="slideButton" onClick={e => (this.videoRecordingIsInOverlay ? this.hideRecording(e) : this.startRecording(e, activeItem))} style={{ fontWeight: 700 }}>
<FontAwesomeIcon icon={`video${this.videoRecordingIsInOverlay ? '-slash' : ''}`} onPointerDown={e => e.stopPropagation()} />
</div>
@@ -622,7 +622,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
}
-Docs.Prototypes.TemplateMap.set(DocumentType.PRESELEMENT, {
- layout: { view: PresElementBox, dataField: 'data' },
- options: { acl: '', title: 'pres element template', _layout_fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' },
+Docs.Prototypes.TemplateMap.set(DocumentType.PRESSLIDE, {
+ layout: { view: PresSlideBox, dataField: 'data' },
+ options: { acl: '', title: 'presSlide', _layout_fitWidth: true, _xMargin: 0, isTemplateDoc: true },
});
diff --git a/src/client/views/nodes/trails/index.ts b/src/client/views/nodes/trails/index.ts
index 7b18974df..a5bc55221 100644
--- a/src/client/views/nodes/trails/index.ts
+++ b/src/client/views/nodes/trails/index.ts
@@ -1,3 +1,3 @@
export * from './PresBox';
-export * from './PresElementBox';
+export * from './PresSlideBox';
export * from './PresEnums';
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index e3ee51424..c45d8e052 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -210,7 +210,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
generateFireflyImage = (imgDesc: string) => {
const selView = DocumentView.Selected().lastElement();
const selDoc = selView?.Document;
- if (selDoc && (selView._props.renderDepth > 1 || selDoc[Doc.LayoutFieldKey(selDoc)] instanceof ImageField)) {
+ if (selDoc && (selView._props.renderDepth > 1 || selDoc[Doc.LayoutDataKey(selDoc)] instanceof ImageField)) {
const oldPrompt = StrCast(selDoc.ai_firefly_prompt, StrCast(selDoc.title));
const newPrompt = oldPrompt ? `${oldPrompt} ~~~ ${imgDesc}` : imgDesc;
return DrawingFillHandler.drawingToImage(selDoc, 100, newPrompt, selDoc)
diff --git a/src/client/views/search/FaceRecognitionHandler.tsx b/src/client/views/search/FaceRecognitionHandler.tsx
index 86e28a768..f60eb486e 100644
--- a/src/client/views/search/FaceRecognitionHandler.tsx
+++ b/src/client/views/search/FaceRecognitionHandler.tsx
@@ -60,7 +60,7 @@ export class FaceRecognitionHandler {
* @param imgDoc image with faces
* @returns faceDoc array
*/
- public static ImageDocFaceAnnos = (imgDoc: Doc) => DocListCast(imgDoc[`${Doc.LayoutFieldKey(imgDoc)}_annotations`]).filter(doc => doc.face);
+ public static ImageDocFaceAnnos = (imgDoc: Doc) => DocListCast(imgDoc[`${Doc.LayoutDataKey(imgDoc)}_annotations`]).filter(doc => doc.face);
/**
* returns a list of all face collection Docs on the current dashboard
@@ -207,7 +207,7 @@ export class FaceRecognitionHandler {
} else if (imgDoc.type === DocumentType.LOADING && !imgDoc.loadingError) {
setTimeout(() => this.classifyFacesInImage(imgDoc), 1000);
} else {
- const imgUrl = ImageCast(imgDoc[Doc.LayoutFieldKey(imgDoc)]);
+ const imgUrl = ImageCast(imgDoc[Doc.LayoutDataKey(imgDoc)]);
if (imgUrl && !DocListCast(Doc.MyFaceCollection.examinedFaceDocs).includes(imgDoc[DocData])) {
// only examine Docs that have an image and that haven't already been examined.
Doc.AddDocToList(Doc.MyFaceCollection, 'examinedFaceDocs', imgDoc[DocData]);
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 1f6e80bd1..ad0f56cb9 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -218,7 +218,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
.filter(d => d)
// eslint-disable-next-line no-loop-func
.map(async d => {
- const fieldKey = Doc.LayoutFieldKey(d);
+ const fieldKey = Doc.LayoutDataKey(d);
const annos = !Field.toString(Doc.LayoutField(d) as FieldType).includes('CollectionView');
const data = d[annos ? fieldKey + '_annotations' : fieldKey];
const dataDocs = await DocListCastAsync(data);
diff --git a/src/client/views/smartdraw/StickerPalette.tsx b/src/client/views/smartdraw/StickerPalette.tsx
index 2260d1f73..0e234e966 100644
--- a/src/client/views/smartdraw/StickerPalette.tsx
+++ b/src/client/views/smartdraw/StickerPalette.tsx
@@ -56,10 +56,10 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
if (!doc.savedAsSticker) {
const docView = DocumentView.getDocumentView(doc);
await docView?.ComponentView?.updateIcon?.(true);
- const { clone } = await Doc.MakeClone(doc);
+ const { clone } = Doc.MakeClone(doc);
clone.title = doc.title;
- const image = ImageCast(doc.icon, ImageCast(clone[Doc.LayoutFieldKey(clone)]))?.url?.href;
- Doc.AddDocToList(Doc.MyStickers, 'data', makeUserTemplateButtonOrImage(clone, image));
+ const image = ImageCast(doc.icon, ImageCast(clone[Doc.LayoutDataKey(clone)]))?.url?.href;
+ Doc.MyStickers && Doc.AddDocToList(Doc.MyStickers, 'data', makeUserTemplateButtonOrImage(clone, image));
doc.savedAsSticker = true;
}
};
@@ -327,9 +327,10 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
);
renderPaletteView = () => (
<>
- {this.renderDoc(Doc.MyStickers, (r: DocumentView) => {
- this._docView = r;
- })}
+ {Doc.MyStickers &&
+ this.renderDoc(Doc.MyStickers, (r: DocumentView) => {
+ this._docView = r;
+ })}
<Button text="Add" icon={<FontAwesomeIcon icon="square-plus" />} color={SettingsManager.userColor} onClick={() => this.setPaletteMode(StickerPaletteMode.create)} />
</>
);
diff --git a/src/debug/Repl.tsx b/src/debug/Repl.tsx
index 8ac502348..37586fcc0 100644
--- a/src/debug/Repl.tsx
+++ b/src/debug/Repl.tsx
@@ -13,7 +13,7 @@ import { makeInterface } from '../fields/Schema';
class Repl extends React.Component {
@observable text: string = '';
- @observable executedCommands: { command: string; result: any }[] = [];
+ @observable executedCommands: { command: string; result: unknown }[] = [];
onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
this.text = e.target.value;
@@ -30,7 +30,7 @@ class Repl extends React.Component {
if (!script.compiled) {
this.executedCommands.push({ command: this.text, result: 'Compile Error' });
} else {
- const result = script.run({ makeInterface }, err => this.executedCommands.push({ command: this.text, result: err.message || err }));
+ const result = script.run({ makeInterface }, err => this.executedCommands.push({ command: this.text, result: err }));
result.success && this.executedCommands.push({ command: this.text, result: result.result });
}
this.text = '';
@@ -40,7 +40,7 @@ class Repl extends React.Component {
@computed
get commands() {
return this.executedCommands.map(command => (
- <div style={{ marginTop: '5px' }}>
+ <div key={command.command} style={{ marginTop: '5px' }}>
<p>{command.command}</p>
{/* <pre>{JSON.stringify(command.result, null, 2)}</pre> */}
<pre>{command.result instanceof RefField || command.result instanceof ObjectField ? 'object' : String(command.result)}</pre>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 2db8d3b85..abce7ed26 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -17,7 +17,7 @@ import { Copy, FieldChanged, HandleUpdate, Id, Parent, ToJavascriptString, ToScr
import { InkEraserTool, InkInkTool, InkTool } from './InkField';
import { List } from './List';
import { ObjectField, serverOpType } from './ObjectField';
-import { PrefetchProxy, ProxyField } from './Proxy';
+import { PrefetchProxy } from './Proxy';
import { FieldId, RefField } from './RefField';
import { RichTextField } from './RichTextField';
import { listSpec } from './Schema';
@@ -47,7 +47,7 @@ export namespace Field {
* @returns string representation of the field
*/
export function toKeyValueString(doc: Doc, key: string, showComputedValue?: boolean, schemaCell?: boolean): string {
- const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+ const cfield = ComputedField.DisableCompute(() => FieldValue(doc[key]));
const valFunc = (field: FieldType): string => {
const res =
field instanceof ComputedField && showComputedValue
@@ -133,13 +133,13 @@ export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) {
return list ? Promise.all(list).then(() => list) : Promise.resolve(defaultValue);
}
export function NumListCast(field: FieldResult, defaultVal: number[] = []) {
- return Cast(field, listSpec('number'), defaultVal);
+ return Cast(field, listSpec('number'), defaultVal)!;
}
export function StrListCast(field: FieldResult, defaultVal: string[] = []) {
- return Cast(field, listSpec('string'), defaultVal);
+ return Cast(field, listSpec('string'), defaultVal)!;
}
export function DocListCast(field: FieldResult, defaultVal: Doc[] = []) {
- return Cast(field, listSpec(Doc), defaultVal).filter(d => d instanceof Doc) as Doc[];
+ return Cast(field, listSpec(Doc), defaultVal)!.filter(d => d instanceof Doc);
}
export enum aclLevel {
@@ -176,13 +176,27 @@ export function updateCachedAcls(doc: Doc) {
}
if (doc.proto instanceof Promise) {
- doc.proto.then(proto => updateCachedAcls(DocCast(proto)));
+ doc.proto.then(proto => DocCast(proto) && updateCachedAcls(DocCast(proto)!));
return doc.proto;
}
}
return undefined;
}
+/**
+ * computes a field name for where to store and expanded template Doc
+ * The format is layout_[ROOT_TEMPLATE_NMAE]_[ROOT_TEMPLATE_CHILD_NAME]_...
+ * @param template the template (either a root or a root child Doc)
+ * @param layoutFieldKey the fieldKey of the container of the template
+ * @returns field key to store expanded template Doc
+ */
+export function expandedFieldName(template: Doc, layoutFieldKey?: string) {
+ const layout_key = !layoutFieldKey?.endsWith(']')
+ ? 'layout' // layout_SOMETHING = SOMETHING => layout_[SOMETHING] = [SOMETHING]
+ : layoutFieldKey; // prettier-ignore
+ const tempTitle = '[' + StrCast(template.title).replace(/^\[(.*)\]$/, '$1') + ']';
+ return `${layout_key}_${tempTitle}`; // prettier-ignore
+}
@scriptingGlobal
@Deserializable('Doc', (obj: unknown) => updateCachedAcls(obj as Doc), ['id'])
export class Doc extends RefField {
@@ -266,22 +280,28 @@ export class Doc extends RefField {
public static set ActiveDashboard(val: Opt<Doc>) { Doc.UserDoc().activeDashboard = val; } // prettier-ignore
public static get MyFilterHotKeys() { return DocListCast(DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter)?.data).filter(key => key.toolType !== "-opts-"); } // prettier-ignore
public static RemFromFilterHotKeys(doc: Doc) {
- return Doc.RemoveDocFromList(DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter), 'data', doc);
+ return (filters => filters && Doc.RemoveDocFromList(filters, 'data', doc))(DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter));
}
public static AddToFilterHotKeys(doc: Doc) {
- return Doc.AddDocToList(DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter), 'data', doc);
+ return (btns => btns && Doc.AddDocToList(btns, 'data', doc))(DocCast(DocCast(Doc.UserDoc().myContextMenuBtns)?.Filter));
}
public static IsInMyOverlay(doc: Doc) { return Doc.MyOverlayDocs.includes(doc); } // prettier-ignore
- public static AddToMyOverlay(doc: Doc) { return Doc.ActiveDashboard ? Doc.AddDocToList(Doc.ActiveDashboard, 'myOverlayDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
- public static RemFromMyOverlay(doc: Doc) { return Doc.ActiveDashboard ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myOverlayDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
- public static AddToMyPublished(doc: Doc) {
+ public static AddToMyOverlay(doc: Doc) {
+ return Doc.ActiveDashboard && Doc.AddDocToList(Doc.ActiveDashboard, 'myOverlayDocs', doc);
+ } // : Doc.AddDocToList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
+ public static RemFromMyOverlay(doc: Doc) {
+ return Doc.ActiveDashboard && Doc.RemoveDocFromList(Doc.ActiveDashboard, 'myOverlayDocs', doc);
+ } // : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
+ public static AddToMyPublished(doc: Doc) {
doc.$title_custom = true;
- doc.$layout_showTitle = 'title';
- Doc.ActiveDashboard ? Doc.AddDocToList(Doc.ActiveDashboard, 'myPublishedDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
- public static RemFromMyPublished(doc: Doc){
+ doc.$layout_showTitle = 'title';
+ Doc.ActiveDashboard && Doc.AddDocToList(Doc.ActiveDashboard, 'myPublishedDocs', doc);
+ } // : Doc.AddDocToList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
+ public static RemFromMyPublished(doc: Doc) {
doc.$title_custom = false;
doc.$layout_showTitle = undefined;
- Doc.ActiveDashboard ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myPublishedDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
+ Doc.ActiveDashboard && Doc.RemoveDocFromList(Doc.ActiveDashboard, 'myPublishedDocs', doc);
+ } // : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
public static IsComicStyle(doc?: Doc) { return doc && Doc.ActiveDashboard && !Doc.IsSystem(doc) && Doc.UserDoc().renderStyle === 'comic' ; } // prettier-ignore
constructor(id?: FieldId, forceSave?: boolean) {
@@ -407,20 +427,17 @@ export class Doc extends RefField {
}
@computed get __DATA__(): Doc {
const self = this[SelfProxy];
- return self.rootDocument && !self.isTemplateForField ? self : Doc.GetProto(Cast(self[DocLayout].rootDocument, Doc, null) || self);
+ return self.rootDocument && !self.isTemplateForField ? self : Doc.GetProto(DocCast(self[DocLayout].rootDocument, self)!);
}
@computed get __LAYOUT__(): Doc {
const self = this[SelfProxy];
const templateLayoutDoc = Cast(Doc.LayoutField(self), Doc, null);
if (templateLayoutDoc) {
- let renderFieldKey: string = '';
const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layout_fieldKey, 'layout')];
- if (typeof layoutField === 'string') {
- [renderFieldKey] = layoutField.split("fieldKey={'")[1].split("'"); // layoutField.split("'")[1];
- } else {
- return Cast(layoutField, Doc, null);
+ if (typeof layoutField !== 'string') {
+ return DocCast(layoutField, self)!;
}
- return Cast(self['layout_' + templateLayoutDoc.title + '(' + renderFieldKey + ')'], Doc, null) || templateLayoutDoc;
+ return DocCast(self[expandedFieldName(templateLayoutDoc)], templateLayoutDoc)!;
}
return self;
}
@@ -597,7 +614,7 @@ export namespace Doc {
// return the doc's proto, but rather recursively searches through the proto inheritance chain
// and returns the document who's proto is undefined or whose proto is marked as a data doc ('isDataDoc').
export function GetProto(doc: Doc): Doc {
- const proto = doc && (Doc.GetT(doc, 'isDataDoc', 'boolean', true) ? doc : DocCast(doc.proto, doc));
+ const proto = doc && (Doc.GetT(doc, 'isDataDoc', 'boolean', true) ? doc : DocCast(doc.proto, doc)!);
return proto === doc ? proto : Doc.GetProto(proto);
}
export function GetDataDoc(doc: Doc): Doc {
@@ -630,7 +647,7 @@ export namespace Doc {
* @returns true if successful, false otherwise.
*/
export function RemoveDocFromList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, ignoreProto = false) {
- const key = fieldKey || Doc.LayoutFieldKey(listDoc);
+ const key = fieldKey || Doc.LayoutDataKey(listDoc);
const list = Doc.Get(listDoc, key, ignoreProto) === undefined ? (listDoc['$' + key] = new List<Doc>()) : Cast(listDoc[key], listSpec(Doc));
if (list) {
const ind = list.indexOf(doc);
@@ -647,7 +664,7 @@ export namespace Doc {
* @returns true if successful, false otherwise.
*/
export function AddDocToList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean, ignoreProto?: boolean) {
- const key = fieldKey || Doc.LayoutFieldKey(listDoc);
+ const key = fieldKey || Doc.LayoutDataKey(listDoc);
const list = Doc.Get(listDoc, key, ignoreProto) === undefined ? (listDoc['$' + key] = new List<Doc>()) : Cast(listDoc[key], listSpec(Doc));
if (list) {
if (!allowDuplicates) {
@@ -686,6 +703,14 @@ export namespace Doc {
return DocListCast(Doc.Get(doc[DocData], 'proto_embeddings', true));
}
+ /**
+ * Makes an embedding of a Doc. This Doc shares the data portion of the origiginal Doc.
+ * If the copied Doc has no prototype, then instead of copying the Doc, this just creates
+ * a new Doc that is a delegate of the original Doc.
+ * @param doc Doc to embed
+ * @param id id to use for embedded Doc
+ * @returns a new Doc that is an embedding of the original Doc
+ */
export function MakeEmbedding(doc: Doc, id?: string) {
const embedding = (!Doc.IsDataProto(doc) && doc.proto) || doc.type === DocumentType.CONFIG ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
embedding.createdFrom = doc;
@@ -705,17 +730,9 @@ export namespace Doc {
// this lists out all the tag ids that can be in a RichTextField that might contain document ids.
// if a document is cloned, we need to make sure to clone all of these referenced documents as well;
- export const DocsInTextFieldIds = ['audioId', 'textId', 'anchorId', 'docId'];
- export async function makeClone(
- doc: Doc,
- cloneMap: Map<string, Doc>,
- linkMap: Map<string, Doc>,
- rtfs: { copy: Doc; key: string; field: RichTextField }[],
- exclusions: string[],
- pruneDocs: Doc[],
- cloneLinks: boolean,
- cloneTemplates: boolean
- ): Promise<Doc> {
+ const FindDocsInRTF = new RegExp(/(audioId|textId|anchorId|docId)"\s*:\s*"(.*?)"/g);
+
+ export function makeClone(doc: Doc, cloneMap: Map<string, Doc>, linkMap: Map<string, Doc>, rtfs: { copy: Doc; key: string; field: RichTextField }[], exclusions: string[], pruneDocs: Doc[], cloneLinks: boolean, cloneTemplates: boolean): Doc {
if (Doc.IsBaseProto(doc) || ((Doc.isTemplateDoc(doc) || Doc.isTemplateForField(doc)) && !cloneTemplates)) {
return doc;
}
@@ -723,80 +740,59 @@ export namespace Doc {
const copy = new Doc(undefined, true);
cloneMap.set(doc[Id], copy);
const filter = [...exclusions, ...StrListCast(doc.cloneFieldFilter)];
- await Promise.all(
- Object.keys(doc).map(async key => {
- if (filter.includes(key)) return;
- const assignKey = (val: Opt<FieldType>) => {
- copy[key] = val;
- };
- const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
- const field = ProxyField.WithoutProxy(() => doc[key]);
- const copyObjectField = async (objField: ObjectField) => {
- const list = await Cast(doc[key], listSpec(Doc));
- const docs = list && (await DocListCastAsync(list))?.filter(d => d instanceof Doc);
- if (docs !== undefined && docs.length) {
- const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates)));
- assignKey(new List<Doc>(clones));
- } else {
- assignKey(ObjectField.MakeCopy(objField));
- if (objField instanceof RichTextField) {
- if (DocsInTextFieldIds.some(id => objField.Data.includes(`"${id}":`))) {
- const docidsearch = new RegExp('(' + DocsInTextFieldIds.map(exp => '(' + exp + ')').join('|') + ')":"([a-z-A-Z0-9_]*)"', 'g');
- const rawdocids = objField.Data.match(docidsearch);
- const docids = rawdocids?.map((str: string) =>
- DocsInTextFieldIds.reduce((output, exp) => output.replace(new RegExp(`${exp}":`, 'g'), ''), str)
- .replace(/"/g, '')
- .trim()
- );
- const results = docids && (await DocServer.GetRefFields(docids));
- const rdocs = results && Array.from(Object.keys(results)).map(rkey => DocCast(results.get(rkey)));
- rdocs?.map(d => d && Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
- rtfs.push({ copy, key, field: objField });
- }
- }
- }
- };
- const docAtKey = doc[key];
+ Object.keys(doc)
+ .filter(key => !filter.includes(key))
+ .map(key => {
+ const assignKey = (val: Opt<FieldType>) => (copy[key] = val);
+
if (key === 'author') {
- assignKey(ClientUtils.CurrentUserEmail());
- } else if (docAtKey instanceof Doc) {
- if (pruneDocs.includes(docAtKey)) {
- // prune doc and do nothing
- } else if (
- !Doc.IsSystem(docAtKey) &&
- (key.startsWith('layout') ||
- ['embedContainer', 'annotationOn', 'proto'].includes(key) || //
- (['link_anchor_1', 'link_anchor_2'].includes(key) && doc.author === ClientUtils.CurrentUserEmail()))
- ) {
- assignKey(await Doc.makeClone(docAtKey, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
- } else {
- assignKey(docAtKey);
+ return assignKey(ClientUtils.CurrentUserEmail());
+ }
+ const cfield = ComputedField.DisableCompute(() => doc[key]);
+ if (cfield instanceof ComputedField) {
+ return assignKey(cfield[Copy]());
+ }
+ const field = doc[key];
+ if (field instanceof Doc) {
+ const doCopy = () => Doc.IsSystem(field) ||
+ !( key.startsWith('layout') ||
+ ['embedContainer', 'annotationOn', 'proto'].includes(key) || //
+ (['link_anchor_1', 'link_anchor_2'].includes(key) && doc.author === ClientUtils.CurrentUserEmail()) ); // prettier-ignore
+ return !pruneDocs.includes(field) &&
+ assignKey(doCopy()
+ ? field //
+ : Doc.makeClone(field, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates)); // prettier-ignore
+ }
+ if (field instanceof RichTextField) {
+ rtfs.push({ copy, key, field });
+ let docId: string | undefined;
+ while ((docId = (FindDocsInRTF.exec(field.Data) ?? [undefined, undefined, undefined])[2])) {
+ const docCopy = DocServer.GetCachedRefField(docId);
+ docCopy && Doc.makeClone(docCopy, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates);
}
- } else if (field instanceof RefField) {
- assignKey(field);
- } else if (cfield instanceof ComputedField) {
- assignKey(cfield[Copy]());
- } else if (field instanceof ObjectField) {
- await copyObjectField(field);
- } else if (field instanceof Promise) {
- // eslint-disable-next-line no-debugger
- debugger; // This shouldn't happen...
- } else {
- assignKey(field);
+ return assignKey(ObjectField.MakeCopy(field));
}
- })
- );
- Array.from(doc[DirectLinks]).forEach(async link => {
+ if (field instanceof ObjectField) {
+ if (DocListCast(field).length) {
+ return assignKey(new List<Doc>(DocListCast(field).map(d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates))));
+ }
+ return assignKey(ObjectField.MakeCopy(field)); // otherwise just copy the field
+ }
+ if (!(field instanceof Promise)) return assignKey(field);
+ // eslint-disable-next-line no-debugger
+ debugger; // This shouldn't happen...
+ });
+ Array.from(doc[DirectLinks]).forEach(link => {
if (
cloneLinks ||
- ((cloneMap.has(DocCast(link.link_anchor_1)?.[Id]) || cloneMap.has(DocCast(DocCast(link.link_anchor_1)?.annotationOn)?.[Id])) &&
- (cloneMap.has(DocCast(link.link_anchor_2)?.[Id]) || cloneMap.has(DocCast(DocCast(link.link_anchor_2)?.annotationOn)?.[Id])))
+ ((cloneMap.has(DocCast(link.link_anchor_1)?.[Id] ?? '') || cloneMap.has(DocCast(DocCast(link.link_anchor_1)?.annotationOn)?.[Id] ?? '')) &&
+ (cloneMap.has(DocCast(link.link_anchor_2)?.[Id] ?? '') || cloneMap.has(DocCast(DocCast(link.link_anchor_2)?.annotationOn)?.[Id] ?? '')))
) {
- linkMap.set(link[Id], await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
+ linkMap.set(link[Id], Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
}
});
copy.cloneOf = doc;
- const cfield = ComputedField.WithoutComputed(() => FieldValue(doc.title));
+ const cfield = ComputedField.DisableCompute(() => FieldValue(doc.title));
if (Doc.Get(copy, 'title', true) && !(cfield instanceof ComputedField)) copy.title = '>:' + doc.title;
cloneMap.set(doc[Id], copy);
@@ -823,25 +819,27 @@ export namespace Doc {
return docs.map(doc => Doc.MakeClone(doc, cloneLinks, cloneTemplates, cloneMap));
}
- export async function MakeClone(doc: Doc, cloneLinks = true, cloneTemplates = true, cloneMap: Map<string, Doc> = new Map()) {
+ /**
+ * Copies a Doc and all of the Docs that it references. This is a deep copy of the Doc.
+ * However, the additional flags allow you to control whether to copy links and templates.
+ * @param doc Doc to clone
+ * @param cloneLinks whether to clone links to this Doc
+ * @param cloneTemplates whether to clone the templates used by this Doc
+ * @param cloneMap a map from the Doc ids of the original Doc to the new Docs
+ * @returns a clone of the original Doc
+ */
+ export function MakeClone(doc: Doc, cloneLinks = true, cloneTemplates = true, cloneMap: Map<string, Doc> = new Map()) {
const linkMap = new Map<string, Doc>();
const rtfMap: { copy: Doc; key: string; field: RichTextField }[] = [];
- const clone = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], doc.embedContainer ? [DocCast(doc.embedContainer)] : [], cloneLinks, cloneTemplates);
+ const clone = Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], DocCast(doc.embedContainer) ? [DocCast(doc.embedContainer)!] : [], cloneLinks, cloneTemplates);
const repaired = new Set<Doc>();
const linkedDocs = Array.from(linkMap.values());
linkedDocs.forEach(link => Doc.AddLink?.(link, true));
rtfMap.forEach(({ copy, key, field }) => {
- const replacer = (match: string, attr: string, id: string /* , offset: any, string: any */) => {
- const mapped = cloneMap.get(id);
- return attr + '"' + (mapped ? mapped[Id] : id) + '"';
- };
- const replacer2 = (match: string, href: string, id: string /* , offset: any, string: any */) => {
- const mapped = cloneMap.get(id);
- return href + (mapped ? mapped[Id] : id);
- };
+ const replacer = (match: string, attr: string, id: string) => attr + '":"' + (cloneMap.get(id)?.[Id] ?? id) + '"';
+ const replacer2 = (match: string, href: string, id: string) => href + (cloneMap.get(id)?.[Id] ?? id);
const re = new RegExp(`(${Doc.localServerPath()})([^"]*)`, 'g');
- const docidsearch = new RegExp('(' + DocsInTextFieldIds.map(exp => `"${exp}":`).join('|') + ')"([^"]+)"', 'g');
- copy[key] = new RichTextField(field.Data.replace(docidsearch, replacer).replace(re, replacer2), field.Text);
+ copy[key] = new RichTextField(field.Data.replace(FindDocsInRTF, replacer).replace(re, replacer2), field.Text);
});
const clonedDocs = [...Array.from(cloneMap.values()), ...linkedDocs];
clonedDocs.forEach(cloneDoc => Doc.repairClone(cloneDoc, cloneMap, cloneTemplates, repaired));
@@ -849,23 +847,29 @@ export namespace Doc {
}
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
- // of the original layout while allowing for individual layout properties to be overridden in the expanded layout.
- export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc) {
+ /**
+ * 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
+ * of the original layout while allowing for individual layout properties to be overridden in the expanded layout
+ * @param templateLayoutDoc a rendering template Doc
+ * @param targetDoc the Doc that the render template will be applied to
+ * @param layoutFieldKey the accumulated layoutFieldKey for the container of this expanded template
+ * @returns a Doc to use to render the targetDoc in the style of the template layout
+ */
+ export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc, layoutFieldKey?: string) {
// nothing to do if the layout isn't a template or we don't have a target that's different than the template
if (!targetDoc || templateLayoutDoc === targetDoc || (!Doc.isTemplateForField(templateLayoutDoc) && !Doc.isTemplateDoc(templateLayoutDoc))) {
return templateLayoutDoc;
}
- const templateField = StrCast(templateLayoutDoc.isTemplateForField, Doc.LayoutFieldKey(templateLayoutDoc)); // the field that the template renders
+ const templateField = StrCast(templateLayoutDoc.isTemplateForField, Doc.LayoutDataKey(templateLayoutDoc)); // the field that the template renders
+
// First it checks if an expanded layout already exists -- if so it will be stored on the dataDoc
// using the template layout doc's id as the field key.
// If it doesn't find the expanded layout, then it makes a delegate of the template layout and
// saves it on the data doc indexed by the template layout's id.
//
- const expandedLayoutFieldKey = 'layout_' + templateLayoutDoc.title + '(' + templateField + ')';
+ const expandedLayoutFieldKey = expandedFieldName(templateLayoutDoc, layoutFieldKey);
let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
if (templateLayoutDoc.rootDocument instanceof Promise) {
@@ -876,7 +880,7 @@ export namespace Doc {
expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params
} else {
// eslint-disable-next-line no-param-reassign
- templateLayoutDoc.rootDocument && (templateLayoutDoc = DocCast(templateLayoutDoc.proto, templateLayoutDoc)); // if the template has already been applied (ie, a nested template), then use the template's prototype
+ templateLayoutDoc.rootDocument && (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.add(targetDoc[Id] + expandedLayoutFieldKey);
setTimeout(
@@ -885,6 +889,7 @@ export namespace Doc {
const dataDoc = Doc.GetProto(targetDoc);
newLayoutDoc.rootDocument = targetDoc;
newLayoutDoc.embedContainer = targetDoc;
+ newLayoutDoc.cloneOnCopy = true;
newLayoutDoc.acl_Guest = SharingPermissions.Edit;
if (dataDoc[templateField] === undefined && (templateLayoutDoc[templateField] as List<Doc>)?.length) {
dataDoc[templateField] = ObjectField.MakeCopy(templateLayoutDoc[templateField] as List<Doc>);
@@ -901,87 +906,78 @@ export namespace Doc {
return expandedTemplateLayout instanceof Doc ? expandedTemplateLayout : undefined; // layout is undefined if the expandedTemplateLayout is pending.
}
- // if the childDoc is a template for a field, then this will return the expanded layout with its data doc.
- // otherwise, it just returns the childDoc
- export function GetLayoutDataDocPair(containerDoc: Doc, containerDataDoc: Opt<Doc>, childDoc: Doc) {
+ /**
+ * Returns a layout and data Doc pair to use to render the specified childDoc of the container.
+ * if the childDoc is a template for a field, then this will return the expanded layout with its data doc.
+ * otherwise, only a layout is returned since that will contain the data as its prototype.
+ * @param containerDoc the template container (that may be nested within a template, the root of a template, or not a template)
+ * @param containerDataDoc the template container's data doc (if the container is nested within the template)
+ * @param childDoc the doc to render
+ * @param layoutFieldKey the accumulated layoutFieldKey for the container of the doc being rendered
+ * @returns a layout Doc to render and an optional data Doc if the layout is a template
+ */
+ export function GetLayoutDataDocPair(containerDoc: Doc, containerDataDoc: Opt<Doc>, childDoc: Doc, layoutFieldKey?: string) {
if (!childDoc || childDoc instanceof Promise || !Doc.GetProto(childDoc)) {
console.log('Warning: GetLayoutDataDocPair childDoc not defined');
return { layout: childDoc, data: childDoc };
}
const data = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!Doc.isTemplateDoc(childDoc) && !Doc.isTemplateForField(childDoc)) ? undefined : containerDataDoc;
const templateRoot = DocCast(containerDoc?.rootDocument);
- return { layout: Doc.expandTemplateLayout(childDoc, templateRoot), data };
+ return { layout: Doc.expandTemplateLayout(childDoc, templateRoot, layoutFieldKey), data };
}
- export function FindReferences(infield: Doc | List<Doc>, references: Set<Doc>, system: boolean | undefined) {
- if (infield instanceof Promise) return;
- if (!(infield instanceof Doc)) {
- infield?.forEach(val => (val instanceof Doc || val instanceof List) && FindReferences(val, references, system));
- return;
+ /**
+ * Recursively travels through all the metadata reachable from the Doc to find all referenced Docs
+ * @param doc Doc to search
+ * @param references all Docs reachable from the Doc
+ * @param system whether to include system Docs
+ * @returns all Docs reachable from the Doc
+ */
+ export function FindReferences(doc: Doc | List<Doc> | undefined, references: Set<Doc>, system: boolean | undefined) {
+ if (!doc || doc instanceof Promise) {
+ return references;
+ }
+ if (!(doc instanceof Doc)) {
+ doc?.forEach(val => (val instanceof Doc || val instanceof List) && FindReferences(val, references, system));
+ return references;
}
- const doc = infield as Doc;
if (references.has(doc)) {
- references.add(doc);
- return;
+ return references;
+ }
+ if (system !== undefined && ((system && !Doc.IsSystem(doc)) || (!system && Doc.IsSystem(doc)))) {
+ return references;
}
- const excludeLists = [Doc.MyRecentlyClosed, Doc.MyHeaderBar, Doc.MyDashboards].includes(doc);
- if (system !== undefined && ((system && !Doc.IsSystem(doc)) || (!system && Doc.IsSystem(doc)))) return;
+
references.add(doc);
- Object.keys(doc).forEach(key => {
- if (key === 'proto') {
- if (doc.proto instanceof Doc) {
- Doc.FindReferences(doc.proto, references, system);
+ Object.keys(doc)
+ .filter(key => key !== 'author' && !(ComputedField.DisableCompute(() => FieldValue(doc[key])) instanceof ComputedField))
+ .map(key => doc[key])
+ .forEach(field => {
+ if (field instanceof List) {
+ return ![Doc.MyRecentlyClosed, Doc.MyHeaderBar, Doc.MyDashboards].includes(doc) && Doc.FindReferences(field, references, system);
}
- } else {
- const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
- const field = key === 'author' ? ClientUtils.CurrentUserEmail() : ProxyField.WithoutProxy(() => doc[key]);
- if (field instanceof RefField) {
- if (field instanceof Doc) {
- if (key === 'myLinkDatabase') {
- field instanceof Doc && references.add(field);
- // skip docs that have been closed and are scheduled for garbage collection
- } else {
- Doc.FindReferences(field, references, system);
- }
- }
- } else if (cfield instanceof ComputedField) {
- /* empty */
- } else if (field instanceof ObjectField) {
- if (field instanceof Doc) {
- Doc.FindReferences(field, references, system);
- } else if (field instanceof List) {
- !excludeLists && Doc.FindReferences(field, references, system);
- } else if (field instanceof ProxyField) {
- if (key === 'myLinkDatabase') {
- field instanceof Doc && references.add(field);
- // skip docs that have been closed and are scheduled for garbage collection
- } else {
- Doc.FindReferences(field.value, references, system);
- }
- } else if (field instanceof PrefetchProxy) {
- Doc.FindReferences(field.value, references, system);
- } else if (field instanceof RichTextField) {
- const re = /"docId"\s*:\s*"(.*?)"/g;
- let match: string[] | null;
- while ((match = re.exec(field.Data)) !== null) {
- const urlString = match[1];
- if (urlString) {
- const rdoc = DocServer.GetCachedRefField(urlString);
- if (rdoc) {
- references.add(rdoc);
- Doc.FindReferences(rdoc, references, system);
- }
- }
- }
+ if (field instanceof Doc) {
+ return Doc.FindReferences(field, references, system);
+ }
+ if (field instanceof RichTextField) {
+ let docId: string | undefined;
+ while ((docId = (FindDocsInRTF.exec(field.Data) ?? [undefined, undefined, undefined])[2])) {
+ Doc.FindReferences(DocServer.GetCachedRefField(docId), references, system);
}
- } else if (field instanceof Promise) {
- // eslint-disable-next-line no-debugger
- debugger; // This shouldn't happend...
}
- }
- });
+ });
+ return references;
}
+ /**
+ * Copies a Doc by copying its embedding (and optionally its prototype). Values within the Doc are copied except for Docs which are
+ * sipmly referenced (except if they're marked to be copied - eg., template layout Docs)
+ * @param doc Doc to copy
+ * @param copyProto whether to copy the Docs proto
+ * @param copyProtoId the id to use for the proto if copied
+ * @param retitle whether to retitle the copy by adding a copy number to the title
+ * @returns the copied Doc
+ */
export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string, retitle = false): Doc {
const copy = runInAction(() => new Doc(copyProtoId, true));
updateCachedAcls(copy);
@@ -994,24 +990,18 @@ export namespace Doc {
copy[key] = Doc.MakeCopy(doc.proto, false);
}
} else {
- const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
- const field = key === 'author' ? ClientUtils.CurrentUserEmail() : ProxyField.WithoutProxy(() => doc[key]);
- if (field instanceof RefField) {
- copy[key] = field;
- } else if (cfield instanceof ComputedField) {
- copy[key] = cfield[Copy](); // ComputedField.MakeFunction(cfield.script.originalScript);
- } else if (field instanceof ObjectField) {
- const docAtKey = doc[key];
- copy[key] =
- docAtKey instanceof Doc && (key.includes('layout[') || docAtKey.cloneOnCopy)
- ? new ProxyField(Doc.MakeCopy(docAtKey)) // copy the expanded render template
- : ObjectField.MakeCopy(field);
- } else if (field instanceof Promise) {
+ const field = key === 'author' ? ClientUtils.CurrentUserEmail() : doc[key];
+ if (field instanceof Promise) {
// eslint-disable-next-line no-debugger
debugger; // This shouldn't happend...
- } else {
- copy[key] = field;
}
+ copy[key] = (() => {
+ const cfield = ComputedField.DisableCompute(() => FieldValue(doc[key]));
+ if (cfield instanceof ComputedField) return cfield[Copy]();
+ if (field instanceof Doc) return field.cloneOnCopy ? Doc.MakeCopy(field) : field; // copy the expanded render template
+ if (field instanceof ObjectField) return ObjectField.MakeCopy(field);
+ return field;
+ })();
}
});
if (copyProto) {
@@ -1027,6 +1017,14 @@ export namespace Doc {
return copy;
}
+ /**
+ * Makes a delegate of a prototype Doc. Delegates inherit all of the properties of the
+ * prototype Doc, but can add new properties or mask existing prototype properties.
+ * @param doc prototype Doc to make a delgate of
+ * @param id id to use for delegate
+ * @param title title to use for delegate
+ * @returns a new Doc that is a delegate of the original Doc
+ */
export function MakeDelegate(doc: Doc, id?: string, title?: string): Doc;
export function MakeDelegate(doc: Opt<Doc>, id?: string, title?: string): Opt<Doc>;
export function MakeDelegate(doc: Opt<Doc>, id?: string, title?: string): Opt<Doc> {
@@ -1096,13 +1094,13 @@ export namespace Doc {
//
export function MakeMetadataFieldTemplate(templateField: Doc, templateDoc: Opt<Doc>, keepFieldKey = false): boolean {
// find the metadata field key that this template field doc will display (indicated by its title)
- const metadataFieldKey = keepFieldKey ? Doc.LayoutFieldKey(templateField) : StrCast(templateField.isTemplateForField) || StrCast(templateField.title).replace(/^-/, '') || Doc.LayoutFieldKey(templateField);
+ const metadataFieldKey = keepFieldKey ? Doc.LayoutDataKey(templateField) : StrCast(templateField.isTemplateForField) || StrCast(templateField.title).replace(/^-/, '') || Doc.LayoutDataKey(templateField);
// update the original template to mark it as a template
templateField.isTemplateForField = metadataFieldKey;
!keepFieldKey && (templateField.title = metadataFieldKey);
- const templateFieldValue = templateField[metadataFieldKey] || templateField[Doc.LayoutFieldKey(templateField)];
+ const templateFieldValue = templateField[metadataFieldKey] || templateField[Doc.LayoutDataKey(templateField)];
// move any data that the template field had been rendering over to the template doc so that things will still be rendered
// when the template field is adjusted to point to the new metadatafield key.
// note 1: if the template field contained a list of documents, each of those documents will be converted to templates as well.
@@ -1148,55 +1146,74 @@ export namespace Doc {
@observable _searchQuery: string = '';
}
- // the document containing the view layout information - will be the Document itself unless the Document has
- // a layout field or 'layout' is given.
- export function Layout(doc: Doc, template?: Doc): Doc {
- const expandedTemplate = template && Cast(doc['layout_' + template.title + '(' + StrCast(template.isTemplateForField, 'data') + ')'], Doc, null);
+ /**
+ * The layout Doc containing the view layout information - this will be :
+ * a) the Doc being rendered itself unless
+ * b) a template Doc stored in the field sepcified by Doc's layout_fieldKey
+ * c) or the specifeid 'template' Doc;
+ * If a template is specified, it will be expanded to create an instance specific to the rendered doc;
+ * @param doc the doc to render
+ * @param template a Doc to use as a template for the layout
+ * @returns
+ */
+ export function LayoutDoc(doc: Doc, template?: Doc): Doc {
+ const expandedTemplate = template && Cast(doc[expandedFieldName(template)], Doc, null);
return expandedTemplate || doc[DocLayout];
}
+ /**
+ * The JSX or Doc value defining how to render the Doc.
+ * @param doc Doc to render
+ * @returns a JSX string or Doc that describes how to render the Doc
+ */
export function LayoutField(doc: Doc) {
return doc[StrCast(doc.layout_fieldKey, 'layout')];
}
- export function LayoutFieldKey(doc: Doc, templateLayoutString?: string): string {
+ /**
+ * The field key of the Doc where the primary Data can be found to render the Doc.
+ * eg., for an image, this is likely to be the 'data' field which contains an image url,
+ * and for a text doc, this is likely to be the 'text' field ontaingin the text of the doc.
+ * @param doc Doc to render
+ * @param templateLayoutString optional JSX string that specifies the doc's data field key
+ * @returns field key where data is stored on Doc
+ */
+ export function LayoutDataKey(doc: Doc, templateLayoutString?: string): string {
const match = StrCast(templateLayoutString || doc[DocLayout].layout).match(/fieldKey={'([^']+)'}/);
- return match?.[1] || ''; // bcz: TODO check on this . used to always reference 'layout', now it uses the layout speicfied by the current layout_fieldKey
+ return match?.[1] || '';
}
export function NativeAspect(doc: Doc, dataDoc?: Doc, useDim?: boolean) {
return Doc.NativeWidth(doc, dataDoc, useDim) / (Doc.NativeHeight(doc, dataDoc, useDim) || 1);
}
export function NativeWidth(doc?: Doc, dataDoc?: Doc, useWidth?: boolean) {
- return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeWidth'], useWidth ? NumCast(doc._width) : 0));
+ return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutDataKey(doc) + '_nativeWidth'], useWidth ? NumCast(doc._width) : 0));
}
export function NativeHeight(doc?: Doc, dataDoc?: Doc, useHeight?: boolean) {
if (!doc) return 0;
const nheight = (Doc.NativeWidth(doc, dataDoc, useHeight) / NumCast(doc._width)) * NumCast(doc._height); // divide before multiply to avoid floating point errrorin case nativewidth = width
- const dheight = NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '_nativeHeight'], useHeight ? NumCast(doc._height) : 0);
+ const dheight = NumCast((dataDoc || doc)[Doc.LayoutDataKey(doc) + '_nativeHeight'], useHeight ? NumCast(doc._height) : 0);
return NumCast(doc._nativeHeight, nheight || dheight);
}
export function SetNativeWidth(doc: Doc, width: number | undefined, fieldKey?: string) {
- doc[(fieldKey || Doc.LayoutFieldKey(doc)) + '_nativeWidth'] = width;
+ doc[(fieldKey || Doc.LayoutDataKey(doc)) + '_nativeWidth'] = width;
}
export function SetNativeHeight(doc: Doc, height: number | undefined, fieldKey?: string) {
- doc[(fieldKey || Doc.LayoutFieldKey(doc)) + '_nativeHeight'] = height;
+ doc[(fieldKey || Doc.LayoutDataKey(doc)) + '_nativeHeight'] = height;
}
const manager = new UserDocData();
- export function SearchQuery(): string {
+ export function SearchQuery() {
return manager._searchQuery;
}
export function SetSearchQuery(query: string) {
- runInAction(() => {
- manager._searchQuery = query;
- });
+ manager._searchQuery = query;
}
- export function UserDoc(): Doc {
+ export function UserDoc() {
return manager._user_doc;
}
- export function SharingDoc(): Doc {
+ export function SharingDoc() {
return Doc.MySharedDocs;
}
- export function LinkDBDoc(): Doc {
- return Cast(Doc.UserDoc().myLinkDatabase, Doc, null);
+ export function LinkDBDoc() {
+ return DocCast(Doc.UserDoc().myLinkDatabase);
}
export function SetUserDoc(doc: Doc) {
return (manager._user_doc = doc);
@@ -1221,7 +1238,7 @@ export namespace Doc {
if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(doc[DocData]) === AclPrivate) return doc;
const result = brushManager.SearchMatchDoc.get(doc);
const num = Math.abs(result?.searchMatch || 0) + 1;
- runInAction(() => result && brushManager.SearchMatchDoc.set(doc, { searchMatch: backward ? -num : num }));
+ result && brushManager.SearchMatchDoc.set(doc, { searchMatch: backward ? -num : num });
return doc;
}
export function ClearSearchMatches() {
@@ -1299,7 +1316,7 @@ export namespace Doc {
highlightedDocs.add(doc[DocData]);
doc[DocData][Highlight] = true;
// want to highlight the targets of presentation docs explicitly since following a pres target does not highlight PDf <Annotations> which are not DocumentViews
- if (doc.presentation_targetDoc) DocCast(doc.presentation_targetDoc)[Highlight] = true;
+ if (DocCast(doc.presentation_targetDoc)) DocCast(doc.presentation_targetDoc)![Highlight] = true;
}
});
}
@@ -1311,7 +1328,7 @@ export namespace Doc {
highlightedDocs.delete(doc[DocData]);
doc[Highlight] = doc[DocData][Highlight] = false;
doc[Animation] = undefined;
- if (doc.presentation_targetDoc) DocCast(doc.presentation_targetDoc)[Highlight] = false;
+ if (DocCast(doc.presentation_targetDoc)) DocCast(doc.presentation_targetDoc)![Highlight] = false;
});
});
}
@@ -1338,7 +1355,7 @@ export namespace Doc {
export function setDocRangeFilter(container: Opt<Doc>, key: string, range?: readonly number[], modifiers?: 'remove') {
if (!container) return;
- const childFiltersByRanges = Cast(container._childFiltersByRanges, listSpec('string'), []);
+ const childFiltersByRanges = StrListCast(container._childFiltersByRanges);
for (let i = 0; i < childFiltersByRanges.length; i += 3) {
if (childFiltersByRanges[i] === key) {
@@ -1346,7 +1363,7 @@ export namespace Doc {
break;
}
}
- if (range !== undefined) {
+ if (range) {
childFiltersByRanges.push(key);
childFiltersByRanges.push(range[0].toString());
childFiltersByRanges.push(range[1].toString());
@@ -1401,7 +1418,7 @@ export namespace Doc {
});
}
export function readDocRangeFilter(doc: Doc, key: string) {
- const childFiltersByRanges = Cast(doc._childFiltersByRanges, listSpec('string'), []);
+ const childFiltersByRanges = StrListCast(doc._childFiltersByRanges);
for (let i = 0; i < childFiltersByRanges.length; i += 3) {
if (childFiltersByRanges[i] === key) {
return [Number(childFiltersByRanges[i + 1]), Number(childFiltersByRanges[i + 2])];
@@ -1436,8 +1453,9 @@ export namespace Doc {
DocServer.GetRefFields(docids).then(async fieldlist => {
const list = Array.from(fieldlist.values())
.map(d => DocCast(d))
- .filter(d => d);
- const docs = clone ? (await Promise.all(Doc.MakeClones(list, false, false))).map(res => res.clone) : list;
+ .filter(d => d)
+ .map(d => d!);
+ const docs = clone ? Doc.MakeClones(list, false, false).map(res => res.clone) : list;
if (ptx !== undefined && pty !== undefined && newPoint !== undefined) {
const firstx = list.length ? NumCast(list[0].x) + ptx - newPoint[0] : 0;
const firsty = list.length ? NumCast(list[0].y) + pty - newPoint[1] : 0;
@@ -1457,16 +1475,16 @@ export namespace Doc {
* @returns
*/
export function getDescription(doc: Doc) {
- const curDescription = StrCast(doc['$' + Doc.LayoutFieldKey(doc) + '_description']);
+ const curDescription = StrCast(doc['$' + Doc.LayoutDataKey(doc) + '_description']);
const docText = (async (tdoc:Doc) => {
switch (tdoc.type) {
case DocumentType.PDF: return curDescription || StrCast(tdoc.text).split(/\s+/).slice(0, 50).join(' '); // first 50 words of pdf text
- case DocumentType.IMG: return curDescription || imageUrlToBase64(ImageCastWithSuffix(tdoc[Doc.LayoutFieldKey(tdoc)], '_o') ?? '')
+ case DocumentType.IMG: return curDescription || imageUrlToBase64(ImageCastWithSuffix(tdoc[Doc.LayoutDataKey(tdoc)], '_o') ?? '')
.then(hrefBase64 => gptImageLabel(hrefBase64, 'Give three to five labels to describe this image.'));
- case DocumentType.RTF: return RTFCast(tdoc[Doc.LayoutFieldKey(tdoc)])?.Text ?? StrCast(tdoc[Doc.LayoutFieldKey(tdoc)]);
+ case DocumentType.RTF: return RTFCast(tdoc[Doc.LayoutDataKey(tdoc)])?.Text ?? StrCast(tdoc[Doc.LayoutDataKey(tdoc)]);
default: return StrCast(tdoc.title).startsWith("Untitled") ? "" : StrCast(tdoc.title);
}}); // prettier-ignore
- return docText(doc).then(text => (doc['$' + Doc.LayoutFieldKey(doc) + '_description'] = text));
+ return docText(doc).then(text => (doc['$' + Doc.LayoutDataKey(doc) + '_description'] = text));
}
// prettier-ignore
@@ -1714,7 +1732,7 @@ export function IdToDoc(id: string) {
return DocCast(DocServer.GetCachedRefField(id));
}
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function idToDoc(id: string): Doc {
+ScriptingGlobals.add(function idToDoc(id: string): Doc | undefined {
return IdToDoc(id);
});
// eslint-disable-next-line prefer-arrow-callback
@@ -1768,7 +1786,7 @@ ScriptingGlobals.add(function docCastAsync(doc: FieldResult): FieldResult<Doc> {
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function activePresentationItem() {
const curPres = Doc.ActivePresentation;
- return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
+ return curPres && DocListCast(curPres[Doc.LayoutDataKey(curPres)])[NumCast(curPres._itemIndex)];
});
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function setDocFilter(container: Doc, key: string, value: string, modifiers: 'match' | 'check' | 'x' | 'remove') {
diff --git a/src/fields/List.ts b/src/fields/List.ts
index 22bbcb9ab..ba03c0d38 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -255,7 +255,7 @@ export class ListImpl<T extends FieldType> extends ObjectField {
if (fields) {
this[SelfProxy].push(...fields);
}
- // eslint-disable-next-line no-constructor-return
+
return list; // need to return the proxy here, otherwise we don't get any of our list handler functions
}
@@ -317,8 +317,8 @@ export class ListImpl<T extends FieldType> extends ObjectField {
// declare List as a type so you can use it in type declarations, e.g., { l: List, ...}
export type List<T extends FieldType> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[];
-// decalre List as a value so you can invoke 'new' on it, e.g., new List<Doc>() (since List<T> IS ListImpl<T>, we can safely cast the 'new' return value to return List<T>)
-// eslint-disable-next-line no-redeclare
+// declare List as a value so you can invoke 'new' on it, e.g., new List<Doc>() (since List<T> IS ListImpl<T>, we can safely cast the 'new' return value to return List<T>)
+// eslint-disable-next-line no-use-before-define
export const List: { new <T extends FieldType>(fields?: T[]): List<T> } = ListImpl as unknown as { new <T extends FieldType>(fields?: T[]): List<T> };
ScriptingGlobals.add('List', List);
diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts
index 48c336e60..21b9fe89b 100644
--- a/src/fields/Proxy.ts
+++ b/src/fields/Proxy.ts
@@ -16,9 +16,26 @@ function deserializeProxy(field: serializedProxyType) {
}
@Deserializable('proxy', (obj: unknown) => deserializeProxy(obj as serializedProxyType))
export class ProxyField<T extends Doc> extends ObjectField {
+ @serializable(primitive())
+ readonly fieldId: string = '';
+
+ private failed = false;
+
+ // This getter/setter and nested object thing is
+ // because mobx doesn't play well with observable proxies
+ @observable.ref
+ private _cache: { readonly field: T | undefined; p: FieldWaiting<T> | undefined } = { field: undefined, p: undefined };
+ private get cache(): { field: T | undefined; p: FieldWaiting<T> | undefined } {
+ return this._cache;
+ }
+ private set cache(val: { field: T | undefined; p: FieldWaiting<T> | undefined }) {
+ runInAction(() => (this._cache = { ...val }));
+ }
+
constructor();
constructor(value: T);
constructor(fieldId: string);
+ constructor(value: T | string);
constructor(value?: T | string) {
super();
if (typeof value === 'string') {
@@ -30,13 +47,12 @@ export class ProxyField<T extends Doc> extends ObjectField {
}
}
- [ToValue](/* doc: any */) {
+ [ToValue]() {
return ProxyField.toValue(this);
}
[Copy]() {
- if (this.cache.field) return new ProxyField<T>(this.cache.field);
- return new ProxyField<T>(this.fieldId);
+ return new ProxyField<T>(this.cache.field ?? this.fieldId);
}
[ToJavascriptString]() {
@@ -46,27 +62,9 @@ export class ProxyField<T extends Doc> extends ObjectField {
return Field.toScriptString(this[ToValue]()?.value as T); // not sure this is quite right since it doesn't recreate a proxy field, but better than 'invalid' ?
}
[ToString]() {
- return Field.toString(this[ToValue]()?.value);
+ return Field.toString(this[ToValue]()?.value as T);
}
- @serializable(primitive())
- readonly fieldId: string = '';
-
- // This getter/setter and nested object thing is
- // because mobx doesn't play well with observable proxies
- @observable.ref
- private _cache: { readonly field: T | undefined; p: FieldWaiting<T> | undefined } = { field: undefined, p: undefined };
- private get cache(): { field: T | undefined; p: FieldWaiting<T> | undefined } {
- return this._cache;
- }
- private set cache(val: { field: T | undefined; p: FieldWaiting<T> | undefined }) {
- runInAction(() => {
- this._cache = { ...val };
- });
- }
-
- private failed = false;
-
@computed get value(): T | undefined | FieldWaiting<T> {
if (this.cache.field) return this.cache.field;
if (this.failed) return undefined;
@@ -94,32 +92,19 @@ export class ProxyField<T extends Doc> extends ObjectField {
return field;
}
}
-
-// eslint-disable-next-line no-redeclare, @typescript-eslint/no-namespace
export namespace ProxyField {
let useProxy = true;
- export function DisableProxyFields() {
+ export function DisableDereference<T>(fn: () => T) {
useProxy = false;
- }
-
- export function EnableProxyFields() {
- useProxy = true;
- }
-
- export function WithoutProxy<T>(fn: () => T) {
- DisableProxyFields();
try {
return fn();
} finally {
- EnableProxyFields();
+ useProxy = true;
}
}
export function toValue(value: { value: unknown }) {
- if (useProxy) {
- return { value: value.value };
- }
- return undefined;
+ return useProxy ? { value: value.value } : undefined;
}
}
@@ -129,5 +114,6 @@ function prefetchValue(proxy: PrefetchProxy<Doc>) {
}
@scriptingGlobal
-@Deserializable('prefetch_proxy', (obj:unknown) => prefetchValue(obj as PrefetchProxy<Doc>))
+// eslint-disable-next-line no-use-before-define
+@Deserializable('prefetch_proxy', (obj: unknown) => prefetchValue(obj as PrefetchProxy<Doc>))
export class PrefetchProxy<T extends Doc> extends ProxyField<T> {}
diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts
index 89e5cda8d..cdd278d67 100644
--- a/src/fields/Schema.ts
+++ b/src/fields/Schema.ts
@@ -1,6 +1,3 @@
-/* eslint-disable guard-for-in */
-/* eslint-disable no-restricted-syntax */
-/* eslint-disable no-redeclare */
/* eslint-disable no-use-before-define */
import { Interface, ToInterface, Cast, ToConstructor, HasTail, Head, Tail, ListSpec, ToType, DefaultFieldConstructor } from './Types';
import { Doc, FieldType } from './Doc';
@@ -16,7 +13,6 @@ type AllToInterface<T extends Interface[]> = {
export const emptySchema = createSchema({});
export const Document = makeInterface(emptySchema);
-// eslint-disable-next-line no-redeclare
export type Document = makeInterface<[typeof emptySchema]>;
export interface InterfaceFunc<T extends Interface[]> {
@@ -36,9 +32,10 @@ export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFu
const proto = new Proxy(
{},
{
- get(target: any, prop, receiver) {
+ get(target: unknown, prop, receiver) {
const field = receiver.doc?.[prop];
if (prop in schema) {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
const desc = prop === 'proto' ? Doc : (schema as any)[prop]; // bcz: proto doesn't appear in schemas ... maybe it should?
if (typeof desc === 'object' && 'defaultVal' in desc && 'type' in desc) {
// defaultSpec
@@ -59,7 +56,7 @@ export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFu
}
return field;
},
- set(target: any, prop, value, receiver) {
+ set(target: unknown, prop, value, receiver) {
receiver.doc && (receiver.doc[prop] = value); // receiver.doc may be undefined as the result of a change in acls
return true;
},
@@ -77,10 +74,10 @@ export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc)
const type = schema[key];
Object.defineProperty(proto, key, {
get() {
- return Cast(this.__doc[key], type as any);
+ return Cast(this.__doc[key], type as never);
},
set(setValue) {
- const value = Cast(setValue, type as any);
+ const value = Cast(setValue, type as never);
if (value !== undefined) {
this.__doc[key] = value;
return;
@@ -89,7 +86,7 @@ export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc)
},
});
}
- return function (doc: any) {
+ return function (doc: unknown) {
if (!(doc instanceof Doc)) {
throw new Error("Currently wrapping a schema in another schema isn't supported");
}
@@ -101,18 +98,18 @@ export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function createSchema<T extends Interface>(schema: T): T & { proto: ToConstructor<Doc> } {
- return undefined as any;
+ return undefined as never;
// (schema as any).proto = Doc;
// return schema as any;
}
export function listSpec<U extends ToConstructor<FieldType>>(type: U): ListSpec<ToType<U>> {
- return { List: type as any }; // TODO Types
+ return { List: type as never }; // TODO Types
}
export function defaultSpec<T extends ToConstructor<FieldType>>(type: T, defaultVal: ToType<T>): DefaultFieldConstructor<ToType<T>> {
return {
- type: type as any,
+ type: type as never,
defaultVal,
};
}
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 6e2d7eb78..37b65684b 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -149,7 +149,6 @@ export class ScriptField extends ObjectField {
...params,
},
transformer,
- typecheck: false,
editable: true,
addReturn: addReturn,
capturedVariables,
@@ -189,14 +188,12 @@ export class ScriptField extends ObjectField {
export class ComputedField extends ScriptField {
static undefined = '__undefined';
static useComputed = true;
- static DisableComputedFields() { this.useComputed = false; } // prettier-ignore
- static EnableComputedFields() { this.useComputed = true; } // prettier-ignore
- static WithoutComputed<T>(fn: () => T) {
- this.DisableComputedFields();
+ static DisableCompute<T>(fn: () => T) {
+ this.useComputed = false;
try {
return fn();
} finally {
- this.EnableComputedFields();
+ this.useComputed = true;
}
}
@@ -239,7 +236,8 @@ export class ComputedField extends ScriptField {
public static MakeInterpolatedNumber(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number, defaultVal: Opt<number>) {
if (!doc[`${fieldKey}_indexed`]) {
const flist = new List<number>(numberRange(curTimecode + 1).map(emptyFunction) as unknown as number[]);
- flist[curTimecode] = Cast(doc[fieldKey], 'number', null);
+ if (Cast(doc[fieldKey], 'number', null) === undefined) delete flist[curTimecode];
+ else flist[curTimecode] = Cast(doc[fieldKey], 'number', null)!;
doc[`${fieldKey}_indexed`] = flist;
}
const getField = ScriptField.CompileScript(`getIndexVal(this['${fieldKey}_indexed'], this.${interpolatorKey}, ${defaultVal})`, {}, true, {});
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index dc7516f38..e6755f828 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -8,7 +8,7 @@ import { ScriptField } from './ScriptField';
import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from './URLField';
// eslint-disable-next-line no-use-before-define
-export type ToConstructor<T extends FieldType> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends List<infer U> ? ListSpec<U> : new (...args: any[]) => T;
+export type ToConstructor<T extends FieldType> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends List<infer U> ? ListSpec<U> : new (...args: never[]) => T;
export type DefaultFieldConstructor<T extends FieldType> = {
type: ToConstructor<T>;
@@ -17,7 +17,7 @@ export type DefaultFieldConstructor<T extends FieldType> = {
// type ListSpec<T extends Field[]> = { List: ToContructor<Head<T>> | ListSpec<Tail<T>> };
export type ListSpec<T extends FieldType> = { List: ToConstructor<T> };
-export type InterfaceValue = ToConstructor<FieldType> | ListSpec<FieldType> | DefaultFieldConstructor<FieldType> | ((doc?: Doc) => any);
+export type InterfaceValue = ToConstructor<FieldType> | ListSpec<FieldType> | DefaultFieldConstructor<FieldType> | ((doc?: Doc) => never);
export type ToType<T extends InterfaceValue> = T extends 'string'
? string
@@ -31,9 +31,9 @@ export type ToType<T extends InterfaceValue> = T extends 'string'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
T extends DefaultFieldConstructor<infer _U>
? never
- : T extends { new (...args: any[]): List<FieldType> }
+ : T extends { new (...args: never[]): List<FieldType> }
? never
- : T extends { new (...args: any[]): infer R }
+ : T extends { new (...args: never[]): infer R }
? R
: T extends (doc?: Doc) => infer R
? R
@@ -41,29 +41,26 @@ export type ToType<T extends InterfaceValue> = T extends 'string'
export interface Interface {
[key: string]: InterfaceValue;
- // [key: string]: ToConstructor<Field> | ListSpec<Field[]>;
}
export type ToInterface<T extends Interface> = {
[P in Exclude<keyof T, 'proto'>]: T[P] extends DefaultFieldConstructor<infer F> ? Exclude<FieldResult<F>, undefined> : FieldResult<ToType<T[P]>>;
};
-// type ListType<U extends Field[]> = { 0: List<ListType<Tail<U>>>, 1: ToType<Head<U>> }[HasTail<U> extends true ? 0 : 1];
-
-export type Head<T extends any[]> = T extends [any, ...any[]] ? T[0] : never;
-export type Tail<T extends any[]> = ((...t: T) => any) extends (_: any, ...tail: infer TT) => any ? TT : [];
-export type HasTail<T extends any[]> = T extends [] | [any] ? false : true;
+export type Head<T extends unknown[]> = T extends [unknown, ...unknown[]] ? T[0] : Interface;
+export type Tail<T extends unknown[]> = ((...t: T) => unknown) extends (_: unknown, ...tail: infer TT) => unknown ? TT : [];
+export type HasTail<T extends unknown[]> = T extends [] | [unknown] ? false : true;
// TODO Allow you to optionally specify default values for schemas, which should then make that field not be partial
-export type WithoutRefField<T extends FieldType> = T extends RefField ? never : T;
+export type WithoutRefField<T extends FieldType> = T extends RefField ? unknown : T;
export type CastCtor = ToConstructor<FieldType> | ListSpec<FieldType>;
type WithoutList<T extends FieldType> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
-export function Cast<T extends CastCtor>(field: FieldResult, ctor: T): FieldResult<ToType<T>>;
-export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal: WithoutList<WithoutRefField<ToType<T>>> | null): WithoutList<ToType<T>>;
+export function Cast<T extends CastCtor>(field: FieldResult, ctor: T): FieldResult<ToType<T>> | undefined;
+export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal: WithoutList<ToType<T>> | null): WithoutList<ToType<T>> | undefined;
export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal?: ToType<T> | null): FieldResult<ToType<T>> | undefined {
if (field instanceof Promise) {
- return defaultVal === undefined ? (field.then(f => Cast(f, ctor) as any) as any) : defaultVal === null ? undefined : defaultVal;
+ return defaultVal === undefined ? (field.then(f => Cast(f, ctor) as ToType<T>) as ToType<T>) : defaultVal === null ? undefined : defaultVal;
}
if (field !== undefined && !(field instanceof Promise)) {
if (typeof ctor === 'string') {
@@ -72,10 +69,12 @@ export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal
}
} else if (typeof ctor === 'object') {
if (field instanceof List) {
- return field as any;
+ return field as ToType<T>;
}
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
} else if (field instanceof (ctor as any)) {
return field as ToType<T>;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
} else if (field instanceof ProxyField && field.value instanceof (ctor as any)) {
return field.value as ToType<T>;
}
@@ -83,57 +82,27 @@ export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal
return defaultVal === null ? undefined : defaultVal;
}
-export function toList(doc: Doc | Doc[]) {
- return doc instanceof Doc ? [doc] : doc;
-}
+export function toList(doc: Doc | Doc[]) { return doc instanceof Doc ? [doc] : doc; } // prettier-ignore
export function DocCast(field: FieldResult, defaultVal?: Doc) {
- const doc = Cast(field, Doc, null);
- return doc && !(doc instanceof Promise) ? doc : (defaultVal as Doc);
-}
-
-export function NumCast(field: FieldResult, defaultVal: number | null = 0) {
- return Cast(field, 'number', defaultVal);
-}
-
-export function StrCast(field: FieldResult, defaultVal: string | null = '') {
- return Cast(field, 'string', defaultVal);
-}
+ return ((doc: Doc | undefined) => (doc && !(doc instanceof Promise) ? doc : defaultVal))(Cast(field, Doc, null));
+}
+export function NumCast (field: FieldResult, defaultVal: number | null = 0) { return Cast(field, 'number', defaultVal)!; } // prettier-ignore
+export function StrCast (field: FieldResult, defaultVal: string | null = '') { return Cast(field, 'string', defaultVal)!; } // prettier-ignore
+export function BoolCast (field: FieldResult, defaultVal: boolean | null = false) { return Cast(field, 'boolean', defaultVal)!; } // prettier-ignore
+export function DateCast (field: FieldResult, defaultVal: DateField | null = null) { return Cast(field, DateField, defaultVal); } // prettier-ignore
+export function RTFCast (field: FieldResult, defaultVal: RichTextField | null = null){ return Cast(field, RichTextField, defaultVal); } // prettier-ignore
+export function ScriptCast(field: FieldResult, defaultVal: ScriptField | null = null) { return Cast(field, ScriptField, defaultVal); } // prettier-ignore
+export function CsvCast (field: FieldResult, defaultVal: CsvField | null = null) { return Cast(field, CsvField, defaultVal); } // prettier-ignore
+export function WebCast (field: FieldResult, defaultVal: WebField | null = null) { return Cast(field, WebField, defaultVal); } // prettier-ignore
+export function VideoCast (field: FieldResult, defaultVal: VideoField | null = null) { return Cast(field, VideoField, defaultVal); } // prettier-ignore
+export function AudioCast (field: FieldResult, defaultVal: AudioField | null = null) { return Cast(field, AudioField, defaultVal); } // prettier-ignore
+export function PDFCast (field: FieldResult, defaultVal: PdfField | null = null) { return Cast(field, PdfField, defaultVal); } // prettier-ignore
+export function ImageCast (field: FieldResult, defaultVal: ImageField | null = null) { return Cast(field, ImageField, defaultVal); } // prettier-ignore
-export function BoolCast(field: FieldResult, defaultVal: boolean | null = false) {
- return Cast(field, 'boolean', defaultVal);
-}
-export function DateCast(field: FieldResult) {
- return Cast(field, DateField, null);
-}
-export function RTFCast(field: FieldResult) {
- return Cast(field, RichTextField, null);
-}
-
-export function ScriptCast(field: FieldResult, defaultVal: ScriptField | null = null) {
- return Cast(field, ScriptField, defaultVal);
-}
-export function CsvCast(field: FieldResult, defaultVal: CsvField | null = null) {
- return Cast(field, CsvField, defaultVal);
-}
-export function WebCast(field: FieldResult, defaultVal: WebField | null = null) {
- return Cast(field, WebField, defaultVal);
-}
-export function VideoCast(field: FieldResult, defaultVal: VideoField | null = null) {
- return Cast(field, VideoField, defaultVal);
-}
-export function AudioCast(field: FieldResult, defaultVal: AudioField | null = null) {
- return Cast(field, AudioField, defaultVal);
-}
-export function PDFCast(field: FieldResult, defaultVal: PdfField | null = null) {
- return Cast(field, PdfField, defaultVal);
-}
-export function ImageCast(field: FieldResult, defaultVal: ImageField | null = null) {
- return Cast(field, ImageField, defaultVal);
-}
export function ImageCastToNameType(field: FieldResult, defaultVal: ImageField | null = null) {
const href = ImageCast(field, defaultVal)?.url.href;
- return href ? [href.replace(/.[^.]*$/, ''), href.split('.').lastElement()] : ["", ""];
+ return href ? [href.replace(/.[^.]*$/, ''), href.split('.').lastElement()] : ['', ''];
}
export function ImageCastWithSuffix(field: FieldResult, suffix: string, defaultVal: ImageField | null = null) {
const [name, type] = ImageCastToNameType(field, defaultVal);
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 205c500a5..799fa3758 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -241,19 +241,19 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
dataDoc[DirectLinks].forEach(link => distributeAcls(key, acl, link, visited, !!allowUpgrade));
- DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => {
+ DocListCast(dataDoc[Doc.LayoutDataKey(dataDoc)]).forEach(d => {
distributeAcls(key, acl, d, visited, !!allowUpgrade);
d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, !!allowUpgrade);
});
- DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '_annotations']).forEach(d => {
+ DocListCast(dataDoc[Doc.LayoutDataKey(dataDoc) + '_annotations']).forEach(d => {
distributeAcls(key, acl, d, visited, !!allowUpgrade);
d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, !!allowUpgrade);
});
- Object.keys(target) // share expanded layout templates (eg, for presElementBox'es )
- .filter(lkey => lkey.includes('layout[') && DocCast(target[lkey]))
- .forEach(lkey => distributeAcls(key, acl, DocCast(target[lkey]), visited, !!allowUpgrade));
+ Object.keys(target) // share expanded layout templates (eg, for PresSlideBox'es )
+ .filter(lkey => lkey.includes('layout_[') && DocCast(target[lkey]))
+ .forEach(lkey => distributeAcls(key, acl, DocCast(target[lkey])!, visited, !!allowUpgrade));
if (GetEffectiveAcl(dataDoc) === AclAdmin) {
dataDoc[key] = acl;