diff options
Diffstat (limited to 'src/client/views/nodes')
23 files changed, 244 insertions, 250 deletions
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'; |
