diff options
Diffstat (limited to 'src/client/views/nodes/DocumentView.tsx')
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 111 |
1 files changed, 64 insertions, 47 deletions
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index ce7cfa5f4..0f2905d5b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,13 +1,12 @@ /* eslint-disable no-use-before-define */ /* eslint-disable react/jsx-props-no-spreading */ -/* eslint-disable jsx-a11y/no-static-element-interactions */ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { Howl } from 'howler'; import { IReactionDisposer, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Fade, JackInTheBox } from 'react-awesome-reveal'; -import { ClientUtils, DivWidth, isTargetChildOf as isParentOf, lightOrDark, returnFalse, returnVal, simulateMouseClick } from '../../../ClientUtils'; +import { ClientUtils, DivWidth, isTargetChildOf as isParentOf, lightOrDark, returnFalse, returnVal, simMouseEvent, simulateMouseClick } from '../../../ClientUtils'; import { Utils, emptyFunction } from '../../../Utils'; import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../fields/Doc'; import { AclAdmin, AclEdit, AclPrivate, Animation, AudioPlay, DocData, DocViews } from '../../../fields/DocSymbols'; @@ -55,13 +54,6 @@ import { PresEffect, PresEffectDirection } from './trails/PresEnums'; import SpringAnimation from './trails/SlideEffect'; import { SpringType, springMappings } from './trails/SpringUtils'; -interface Window { - MediaRecorder: MediaRecorder; -} -declare class MediaRecorder { - constructor(e: any); // whatever MediaRecorder has -} - export interface DocumentViewProps extends FieldViewSharedProps { hideDecorations?: boolean; // whether to suppress all DocumentDecorations when doc is selected hideResizeHandles?: boolean; // whether to suppress resized handles on doc decorations when this document is selected @@ -89,7 +81,7 @@ export interface DocumentViewProps extends FieldViewSharedProps { dragStarting?: () => void; dragEnding?: () => void; - parent?: any; // parent React component view (see CollectionFreeFormDocumentView) + reactParent?: React.Component; // parent React component view (see CollectionFreeFormDocumentView) } @observer export class DocumentViewInternal extends DocComponent<FieldViewProps & DocumentViewProps>() { @@ -105,7 +97,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document private _disposers: { [name: string]: IReactionDisposer } = {}; private _doubleClickTimeout: NodeJS.Timeout | undefined; - private _singleClickFunc: undefined | (() => any); + private _singleClickFunc: undefined | (() => void); private _longPressSelector: NodeJS.Timeout | undefined; private _downX: number = 0; private _downY: number = 0; @@ -134,16 +126,16 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document animateScaleTime = () => this._animateScaleTime ?? 100; style = (doc: Doc, sprop: StyleProp | string) => this._props.styleProvider?.(doc, this._props, sprop); - @computed get opacity() { return this.style(this.layoutDoc, StyleProp.Opacity); } // prettier-ignore - @computed get boxShadow() { return this.style(this.layoutDoc, StyleProp.BoxShadow); } // prettier-ignore - @computed get borderRounding() { return this.style(this.layoutDoc, StyleProp.BorderRounding); } // prettier-ignore - @computed get widgetDecorations() { return this.style(this.layoutDoc, StyleProp.Decorations); } // prettier-ignore - @computed get backgroundBoxColor(){ return this.style(this.layoutDoc, StyleProp.BackgroundColor + ':docView'); } // prettier-ignore + @computed get opacity() { return this.style(this.layoutDoc, StyleProp.Opacity) as number; } // prettier-ignore + @computed get boxShadow() { return this.style(this.layoutDoc, StyleProp.BoxShadow) as string; } // prettier-ignore + @computed get borderRounding() { return this.style(this.layoutDoc, StyleProp.BorderRounding) as string; } // prettier-ignore + @computed get widgetDecorations() { return this.style(this.layoutDoc, StyleProp.Decorations) as JSX.Element; } // prettier-ignore + @computed get backgroundBoxColor(){ return this.style(this.layoutDoc, StyleProp.BackgroundColor + ':docView') as string; } // prettier-ignore @computed get showTitle() { return this.style(this.layoutDoc, StyleProp.ShowTitle) as Opt<string>; } // prettier-ignore - @computed get showCaption() { return this.style(this.layoutDoc, StyleProp.ShowCaption) ?? 0; } // prettier-ignore - @computed get headerMargin() { return this.style(this.layoutDoc, StyleProp.HeaderMargin) ?? 0; } // prettier-ignore - @computed get titleHeight() { return this.style(this.layoutDoc, StyleProp.TitleHeight) ?? 0; } // prettier-ignore - @computed get docContents() { return this.style(this.Document, StyleProp.DocContents); } // prettier-ignore + @computed get showCaption() { return this.style(this.layoutDoc, StyleProp.ShowCaption) as string ?? ""; } // prettier-ignore + @computed get headerMargin() { return this.style(this.layoutDoc, StyleProp.HeaderMargin) as number ?? 0; } // prettier-ignore + @computed get titleHeight() { return this.style(this.layoutDoc, StyleProp.TitleHeight) as number ?? 0; } // prettier-ignore + @computed get docContents() { return this.style(this.Document, StyleProp.DocContents) as JSX.Element; } // prettier-ignore @computed get highlighting() { return this.style(this.Document, StyleProp.Highlighting); } // prettier-ignore @computed get borderPath() { return this.style(this.Document, StyleProp.BorderPath); } // prettier-ignore @@ -164,13 +156,13 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document /// disable pointer events on content when there's an enabled onClick script (and not in explore mode) and the contents aren't forced active, or if contents are marked inactive @computed get _contentPointerEvents() { TraceMobx(); - return this._props.contentPointerEvents ?? + return (this._props.contentPointerEvents ?? ((!this.disableClickScriptFunc && // this.onClickHdlr && !SnappingManager.ExploreMode && !this.layoutDoc.layout_isSvg && this.isContentActive() !== true) || - this.isContentActive() === false) + this.isContentActive() === false)) ? 'none' : this._pointerEvents; } @@ -224,7 +216,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document { fireImmediately: true } ); this._disposers.pointerevents = reaction( - () => this.style(this.Document, StyleProp.PointerEvents), + () => this.style(this.Document, StyleProp.PointerEvents) as 'all' | 'none' | 'visiblePainted' | undefined, pointerevents => { this._pointerEvents = pointerevents; }, @@ -450,7 +442,11 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document if (this.Document === Doc.ActiveDashboard) { e.stopPropagation(); e.preventDefault(); - alert((e.target as any)?.closest?.('*.lm_content') ? "You can't perform this move most likely because you didn't drag the document's title bar to enable embedding in a different document." : 'Linking to document tabs not yet supported.'); + alert( + (e.target as HTMLElement)?.closest?.('*.lm_content') + ? "You can't perform this move most likely because you didn't drag the document's title bar to enable embedding in a different document." + : 'Linking to document tabs not yet supported.' + ); return true; } const annoData = de.complete.annoDragData; @@ -532,9 +528,9 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document } const cm = ContextMenu.Instance; - if (!cm || (e as any)?.nativeEvent?.SchemaHandled || SnappingManager.ExploreMode) return; + if (!cm || SnappingManager.ExploreMode) return; - if (e && !(e.nativeEvent as any).dash) { + if (e && !(e.nativeEvent instanceof simMouseEvent ? e.nativeEvent.dash : false)) { const onDisplay = () => { if (this.Document.type !== DocumentType.MAP) DocumentViewInternal.SelectAfterContextMenu && this._props.select(false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear. setTimeout(() => simulateMouseClick(document.elementFromPoint(e.clientX, e.clientY), e.clientX, e.clientY, e.screenX, e.screenY)); @@ -722,7 +718,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document anchorPanelWidth = () => this._props.PanelWidth() || 1; anchorPanelHeight = () => this._props.PanelHeight() || 1; - anchorStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string): any => { + anchorStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => { // prettier-ignore switch (property.split(':')[0]) { case StyleProp.ShowTitle: return ''; @@ -770,7 +766,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document captionStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => this._props?.styleProvider?.(doc, props, property + ':caption'); fieldsDropdown = (placeholder: string) => ( <div - ref={action((r: any) => { r && (this._titleDropDownInnerWidth = DivWidth(r));} )} // prettier-ignore + ref={r => { r && runInAction(() => (this._titleDropDownInnerWidth = DivWidth(r)));}} // prettier-ignore onPointerDown={action(() => { this._changingTitleField = true; })} // prettier-ignore style={{ width: 'max-content', background: SnappingManager.userBackgroundColor, color: SnappingManager.userColor, transformOrigin: 'left', transform: `scale(${this.titleHeight / 30 /* height of Dropdown */})` }}> <FieldsDropdown @@ -897,7 +893,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document const showTitle = this.showTitle?.split(':')[0]; return !DocCast(this.Document) || GetEffectiveAcl(this.dataDoc) === AclPrivate ? null - : this.docContents ?? ( + : (this.docContents ?? ( <div className="documentView-node" id={this.Document.type !== DocumentType.LINK ? this._docView?.DocUniqueId : undefined} @@ -923,27 +919,33 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document )} {this.widgetDecorations ?? null} </div> - ); + )); }; render() { TraceMobx(); const { highlighting, borderPath } = this; + const { highlightIndex, highlightStyle, highlightColor, highlightStroke } = (highlighting as { highlightIndex: number; highlightStyle: string; highlightColor: string; highlightStroke: boolean }) ?? { + highlightIndex: undefined, + highlightStyle: undefined, + highlightColor: undefined, + highlightStroke: undefined, + }; + const { clipPath, jsx } = (borderPath as { clipPath: string; jsx: JSX.Element }) ?? { clipPath: undefined, jsx: undefined }; const boxShadow = !highlighting ? this.boxShadow - : highlighting && this.borderRounding && highlighting.highlightStyle !== 'dashed' - ? `0 0 0 ${highlighting.highlightIndex}px ${highlighting.highlightColor}` + : highlighting && this.borderRounding && highlightStyle !== 'dashed' + ? `0 0 0 ${highlightIndex}px ${highlightColor}` : this.boxShadow || (this.Document.isTemplateForField ? 'black 0.2vw 0.2vw 0.8vw' : undefined); const renderDoc = this.renderDoc({ borderRadius: this.borderRounding, - outline: highlighting && !this.borderRounding && !highlighting.highlightStroke ? `${highlighting.highlightColor} ${highlighting.highlightStyle} ${highlighting.highlightIndex}px` : 'solid 0px', - border: highlighting && this.borderRounding && highlighting.highlightStyle === 'dashed' ? `${highlighting.highlightStyle} ${highlighting.highlightColor} ${highlighting.highlightIndex}px` : undefined, + outline: highlighting && !this.borderRounding && !highlightStroke ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : 'solid 0px', + border: highlighting && this.borderRounding && highlightStyle === 'dashed' ? `${highlightStyle} ${highlightColor} ${highlightIndex}px` : undefined, boxShadow, - clipPath: borderPath?.clipPath, + clipPath, }); return ( - // eslint-disable-next-line jsx-a11y/click-events-have-key-events <div className={`${DocumentView.ROOT_DIV} docView-hack`} ref={this._mainCont} @@ -957,8 +959,8 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document borderRadius: this.borderRounding, pointerEvents: this._pointerEvents === 'visiblePainted' ? 'none' : this._pointerEvents, // visible painted means that the underlying doc contents are irregular and will process their own pointer events (otherwise, the contents are expected to fill the entire doc view box so we can handle pointer events here) }}> - {this._componentView?.isUnstyledView?.() || this.Document.type === DocumentType.CONFIG ? renderDoc : DocumentViewInternal.AnimationEffect(renderDoc, this.Document[Animation], this.Document)} - {borderPath?.jsx} + {this._componentView?.isUnstyledView?.() || this.Document.type === DocumentType.CONFIG || !renderDoc ? renderDoc : DocumentViewInternal.AnimationEffect(renderDoc, this.Document[Animation], this.Document)} + {jsx} </div> ); } @@ -968,7 +970,22 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document * @param presEffectDoc presentation effects document that specifies the animation effect parameters * @returns a function that will wrap a JSX animation element wrapping any JSX element */ - public static AnimationEffect(renderDoc: JSX.Element, presEffectDoc: Opt<Doc>, root: Doc) { + public static AnimationEffect( + renderDoc: JSX.Element, + presEffectDoc: Opt< + | Doc + | { + presentation_effectDirection?: string; + followLinkAnimDirection?: string; + presentation_transition?: number; + followLinkTransitionTime?: number; + presentation_effectTiming?: number; + presentation_effect?: string; + followLinkAnimEffect?: string; + } + >, + root: Doc + ) { const dir = ((presEffectDoc?.presentation_effectDirection ?? presEffectDoc?.followLinkAnimDirection) || PresEffectDirection.Center) as PresEffectDirection; const duration = Cast(presEffectDoc?.presentation_transition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null)); const effectProps = { @@ -982,7 +999,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document }; const timing = StrCast(presEffectDoc?.presentation_effectTiming); - const timingConfig = (timing ? JSON.parse(timing) : undefined) ?? { + const timingConfig = (timing ? JSON.parse(timing) : undefined) ?? { type: SpringType.GENTLE, ...springMappings.gentle, }; @@ -1054,7 +1071,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { public static allViews: () => DocumentView[]; public static addView: (dv: DocumentView) => void | undefined; public static removeView: (dv: DocumentView) => void | undefined; - public static addViewRenderedCb: (doc: Opt<Doc>, func: (dv: DocumentView) => any) => boolean; + public static addViewRenderedCb: (doc: Opt<Doc>, func: (dv: DocumentView) => void) => boolean; public static getViews = (doc?: Doc) => Array.from(doc?.[DocViews] ?? []) as DocumentView[]; public static getFirstDocumentView: (toFind: Doc) => DocumentView | undefined; public static getDocumentView: (target: Doc | undefined, preferredCollection?: DocumentView) => Opt<DocumentView>; @@ -1107,7 +1124,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { @observable private _htmlOverlayText: Opt<string> = undefined; @observable private _isHovering = false; @observable private _selected = false; - @observable public static CurrentlyPlaying: DocumentView[] = []; // audio or video media views that are currently playing + @observable public static CurrentlyPlaying: DocumentView[] = []; // audio or video media views that are currently playing @computed private get shouldNotScale() { return (this.layout_fitWidth && !this.nativeWidth) || this.ComponentView?.isUnstyledView?.(); @@ -1254,7 +1271,6 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { } public playAnnotation = () => { - const self = this; const audioAnnoState = this.dataDoc.audioAnnoState ?? AudioAnnoState.stopped; const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '_audioAnnotations'], listSpec(AudioField), null); const anno = audioAnnos?.lastElement(); @@ -1267,7 +1283,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { autoplay: true, loop: false, volume: 0.5, - onend: action(() => { self.dataDoc.audioAnnoState = AudioAnnoState.stopped; }), // prettier-ignore + onend: action(() => { this.dataDoc.audioAnnoState = AudioAnnoState.stopped; }), // prettier-ignore }); this.dataDoc.audioAnnoState = AudioAnnoState.playing; break; @@ -1426,9 +1442,10 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { <div className="documentView-htmlOverlayInner" style={{ transition: `all 500ms`, opacity: this._enableHtmlOverlayTransitions ? 0.9 : 0 }}> {DocumentViewInternal.AnimationEffect( <div className="webBox-textHighlight"> + {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */} <ObserverJsxParser autoCloseVoidElements key={42} onError={(e: any) => console.log('PARSE error', e)} renderInWrapper={false} jsx={StrCast(this._htmlOverlayText)} /> </div>, - { ...(this._htmlOverlayEffect ?? {}), presentation_effect: effect ?? PresEffect.Expand } as any as Doc, + { ...(this._htmlOverlayEffect ?? {}), presentation_effect: effect ?? PresEffect.Expand }, this.Document )} </div> @@ -1457,11 +1474,11 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { style={{ transform: `translate(${this.centeringX}px, ${this.centeringY}px)`, width: xshift ?? `${this._props.PanelWidth() - this.Xshift * 2}px`, - height: this._props.forceAutoHeight ? undefined : yshift ?? (this.layout_fitWidth ? `${this.panelHeight}px` : `${(this.effectiveNativeHeight / this.effectiveNativeWidth) * this._props.PanelWidth()}px`), + height: this._props.forceAutoHeight ? undefined : (yshift ?? (this.layout_fitWidth ? `${this.panelHeight}px` : `${(this.effectiveNativeHeight / this.effectiveNativeWidth) * this._props.PanelWidth()}px`)), }}> <DocumentViewInternal {...this._props} - parent={undefined} + reactParent={undefined} isHovering={this.isHovering} fieldKey={this.LayoutFieldKey} DataTransition={this.DataTransition} |