diff options
Diffstat (limited to 'src')
17 files changed, 215 insertions, 175 deletions
diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts index 630d7edbc..fc415d589 100644 --- a/src/ClientUtils.ts +++ b/src/ClientUtils.ts @@ -7,11 +7,11 @@ import { CollectionViewType, DocumentType } from './client/documents/DocumentTyp import { Colors } from './client/views/global/globalEnums'; import { CreateImage } from './client/views/nodes/WebBoxRenderer'; -export function DashColor(color: string) { +export function DashColor(color: string | undefined) { try { return color ? Color(color.toLowerCase()) : Color('transparent'); } catch (e) { - if (color.includes('gradient')) console.log("using color 'white' in place of :" + color); + if (color?.includes('gradient')) console.log("using color 'white' in place of :" + color); else console.log('COLOR error:', e); return Color('white'); } @@ -455,7 +455,7 @@ export function addStyleSheet() { const sheets = document.head.appendChild(style); return sheets.sheet; } -export function addStyleSheetRule(sheet: CSSStyleSheet | null, selector: string, css: string | {[key:string]: string}, selectorPrefix = '.') { +export function addStyleSheetRule(sheet: CSSStyleSheet | null, selector: string, css: string | { [key: string]: string }, selectorPrefix = '.') { const propText = typeof css === 'string' ? css @@ -464,14 +464,14 @@ export function addStyleSheetRule(sheet: CSSStyleSheet | null, selector: string, .join(';'); return sheet?.insertRule(selectorPrefix + selector + '{' + propText + '}', sheet.cssRules.length); } -export function removeStyleSheetRule(sheet: CSSStyleSheet|null, rule: number) { +export function removeStyleSheetRule(sheet: CSSStyleSheet | null, rule: number) { if (sheet?.rules.length) { sheet.removeRule(rule); return true; } return false; } -export function clearStyleSheetRules(sheet: CSSStyleSheet|null) { +export function clearStyleSheetRules(sheet: CSSStyleSheet | null) { if (sheet?.rules.length) { numberRange(sheet.rules.length).map(() => sheet.removeRule(0)); return true; @@ -479,10 +479,16 @@ export function clearStyleSheetRules(sheet: CSSStyleSheet|null) { return false; } +export class simPointerEvent extends PointerEvent { + dash?: boolean; +} +export class simMouseEvent extends MouseEvent { + dash?: boolean; +} export function simulateMouseClick(element: Element | null | undefined, x: number, y: number, sx: number, sy: number, rightClick = true) { if (!element) return; ['pointerdown', 'pointerup'].forEach(event => { - const me = new PointerEvent(event, { + const me = new simPointerEvent(event, { view: window, bubbles: true, cancelable: true, @@ -493,12 +499,12 @@ export function simulateMouseClick(element: Element | null | undefined, x: numbe screenX: sx, screenY: sy, }); - (me as any).dash = true; + me.dash = true; element.dispatchEvent(me); }); if (rightClick) { - const me = new MouseEvent('contextmenu', { + const me = new simMouseEvent('contextmenu', { view: window, bubbles: true, cancelable: true, @@ -510,7 +516,7 @@ export function simulateMouseClick(element: Element | null | undefined, x: numbe screenX: sx, screenY: sy, }); - (me as any).dash = true; + me.dash = true; element.dispatchEvent(me); } } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 96b8b5657..e41546d09 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -356,7 +356,6 @@ export class DocumentManager { if (options.playMedia) docView.ComponentView?.playFrom?.(NumCast(docView.Document._layout_currentTimecode)); if (options.playAudio) DocumentManager.playAudioAnno(docView.Document); if (options.toggleTarget && (!options.didMove || docView.Document.hidden)) docView.Document.hidden = !docView.Document.hidden; - Doc.AddUnHighlightWatcher(() => docView.Document[Animation] = undefined); } } } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 55f28f415..784d252a3 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -47,6 +47,7 @@ import { FormattedTextBox, FormattedTextBoxProps } from './nodes/formattedText/F import { PinDocView, PinProps } from './PinFuncs'; import { StyleProp } from './StyleProp'; +// eslint-disable-next-line @typescript-eslint/no-var-requires const { INK_MASK_SIZE } = require('./global/globalCssVariables.module.scss'); // prettier-ignore @observer @@ -292,7 +293,7 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>() * @param boundsTop the screen space top coordinate of the ink stroke * @returns the JSX controls for displaying an editing UI for the stroke (control point & tangent handles) */ - componentUI = (boundsLeft: number, boundsTop: number) => { + componentUI = (boundsLeft: number, boundsTop: number): null | JSX.Element => { const inkDoc = this.Document; const { inkData, inkStrokeWidth } = this.inkScaledData(); const screenSpaceCenterlineStrokeWidth = Math.min(3, inkStrokeWidth * this.ScreenToLocalBoxXf().inverse().Scale); // the width of the blue line widget that shows the centerline of the ink stroke @@ -344,12 +345,12 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>() }; @computed get fillColor(): string { const isInkMask = BoolCast(this.layoutDoc.stroke_isInkMask); - return isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FillColor) ?? 'transparent'; + return isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : ((this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FillColor) as 'string') ?? 'transparent'); } @computed get strokeColor() { const { inkData } = this.inkScaledData(); const { fillColor } = this; - return !InkingStroke.IsClosed(inkData) && fillColor && fillColor !== 'transparent' ? fillColor : this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) ?? StrCast(this.layoutDoc.color); + return !InkingStroke.IsClosed(inkData) && fillColor && fillColor !== 'transparent' ? fillColor : ((this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as 'string') ?? StrCast(this.layoutDoc.color)); } render() { TraceMobx(); @@ -370,8 +371,8 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>() }); } const highlight = !this.controlUndo && this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Highlighting); - const highlightIndex = highlight?.highlightIndex; - const highlightColor = !this._props.isSelected() && !isInkMask && highlight?.highlightIndex ? highlight?.highlightColor : undefined; + const { highlightIndex, highlightColor: hColor } = (highlight as { highlightIndex?: number; highlightColor?: string }) ?? { highlightIndex: undefined, highlightColor: undefined }; + const highlightColor = !this._props.isSelected() && !isInkMask && highlightIndex ? hColor : undefined; const color = StrCast(this.layoutDoc.stroke_outlineColor, !closed && fillColor && fillColor !== 'transparent' ? StrCast(this.layoutDoc.color, 'transparent') : 'transparent'); // Visually renders the polygonal line made by the user. @@ -401,12 +402,12 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>() ); const higlightMargin = Math.min(12, Math.max(2, 0.3 * inkStrokeWidth)); // Invisible polygonal line that enables the ink to be selected by the user. - const clickableLine = (downHdlr?: (e: React.PointerEvent) => void, mask: boolean = false): any => + const clickableLine = (downHdlr?: (e: React.PointerEvent) => void, mask: boolean = false) => InteractionUtils.CreatePolyline( inkData, inkLeft, inkTop, - mask && color === 'transparent' ? this.strokeColor : highlightColor ?? color, + mask && color === 'transparent' ? this.strokeColor : (highlightColor ?? color), inkStrokeWidth, inkStrokeWidth + NumCast(this.layoutDoc.stroke_borderWidth) + (fillColor ? (closed ? higlightMargin : (highlightIndex ?? 0) + higlightMargin) : higlightMargin), StrCast(this.layoutDoc.stroke_lineJoin), @@ -420,7 +421,7 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>() inkScaleX, inkScaleY, '', - this._props.pointerEvents?.() ?? 'visiblepainted', + this._props.pointerEvents?.() ?? 'visiblePainted', 0.0, false, downHdlr, diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 8c100f238..8a07a6bd7 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -1,6 +1,3 @@ -/* eslint-disable jsx-a11y/alt-text */ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; @@ -24,7 +21,7 @@ import { undoBatch, UndoManager } from '../util/UndoManager'; import { TreeSort } from './collections/TreeSort'; import { Colors } from './global/globalEnums'; import { DocumentView, DocumentViewProps } from './nodes/DocumentView'; -import { FieldViewProps } from './nodes/FieldView'; +import { FieldViewProps, StyleProviderFuncType } from './nodes/FieldView'; import { StyleProp } from './StyleProp'; import './StyleProvider.scss'; @@ -43,9 +40,9 @@ function togglePaintView(e: React.MouseEvent, doc: Opt<Doc>, props: Opt<FieldVie } export function styleFromLayoutString(doc: Doc, props: FieldViewProps, scale: number) { - const style: { [key: string]: any } = {}; + const style: { [key: string]: string } = {}; const divKeys = ['width', 'height', 'fontSize', 'transform', 'left', 'backgroundColor', 'left', 'right', 'top', 'bottom', 'pointerEvents', 'position']; - const replacer = (match: any, expr: string) => + const replacer = (match: string, expr: string) => // bcz: this executes a script to convert a property expression string: { script } into a value ScriptField.MakeFunction(expr, { this: Doc.name, scale: 'number' })?.script.run({ this: doc, scale }).result?.toString() ?? ''; divKeys.forEach((prop: string) => { @@ -72,7 +69,7 @@ export function SetFilterOpener(func: () => void) { // a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab // -export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & DocumentViewProps>, property: string): any { +export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & DocumentViewProps>, property: string) : StyleProviderFuncType { const remoteDocHeader = 'author;author_date;noMargin'; const isCaption = property.includes(':caption'); const isAnchor = property.includes(':anchor'); @@ -110,9 +107,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & const lockedPosition = () => doc && BoolCast(doc._lockedPosition); const titleHeight = () => styleProvider?.(doc, props, StyleProp.TitleHeight); const backgroundCol = () => styleProvider?.(doc, props, StyleProp.BackgroundColor + ':nonTransparent' + (isNonTransparentLevel + 1)); - const color = () => styleProvider?.(doc, props, StyleProp.Color); + const color = () => styleProvider?.(doc, props, StyleProp.Color) as string; const opacity = () => styleProvider?.(doc, props, StyleProp.Opacity); - const layoutShowTitle = () => styleProvider?.(doc, props, StyleProp.ShowTitle); + const layoutShowTitle = () => styleProvider?.(doc, props, StyleProp.ShowTitle) as string; // prettier-ignore switch (property.split(':')[0]) { case StyleProp.TreeViewIcon: { @@ -144,7 +141,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & highlightStyle: doc.isGroup ? "dotted": highlightStyle, highlightColor, highlightIndex, - highlightStroke: layoutDoc?.layout_isSvg, + highlightStroke: BoolCast(layoutDoc?.layout_isSvg), }; } } @@ -152,7 +149,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & case StyleProp.DocContents: return undefined; case StyleProp.WidgetColor: return isAnnotated ? Colors.LIGHT_BLUE : 'dimgrey'; case StyleProp.Opacity: return componentView?.isUnstyledView?.() ? 1 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null)); - case StyleProp.FontColor: return StrCast(doc?.[fieldKey + 'fontColor'], isCaption ? lightOrDark(backgroundCol()) : StrCast(Doc.UserDoc().fontColor, color())); + case StyleProp.FontColor: return StrCast(doc?.[fieldKey + 'fontColor'], isCaption ? lightOrDark(backgroundCol()) : StrCast(Doc.UserDoc().fontColor, color())); case StyleProp.FontSize: return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(Doc.UserDoc().fontSize)); case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(Doc.UserDoc().fontFamily)); case StyleProp.FontWeight: return StrCast(doc?.[fieldKey + 'fontWeight'], StrCast(Doc.UserDoc().fontWeight)); @@ -168,7 +165,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & StrCast( doc._layout_showTitle, showTitle?.() || - (!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.FUNCPLOT, DocumentType.LABEL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as any) + (!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.FUNCPLOT, DocumentType.LABEL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as DocumentType) ? doc.author === ClientUtils.CurrentUserEmail() ? StrCast(Doc.UserDoc().layout_showTitle) : remoteDocHeader @@ -207,7 +204,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & }; } case StyleProp.HeaderMargin: - return ([CollectionViewType.Stacking, CollectionViewType.NoteTaking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._type_collection as any) || + return ([CollectionViewType.Stacking, CollectionViewType.NoteTaking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._type_collection as CollectionViewType) || (doc?.type === DocumentType.RTF && !layoutShowTitle()?.includes('noMargin')) || doc?.type === DocumentType.LABEL) && layoutShowTitle() && @@ -297,8 +294,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & if (SnappingManager.ExploreMode || doc?.layout_unrendered) return isInk() ? 'visiblePainted' : 'all'; if (pointerEvents?.() === 'none') return 'none'; if (opacity() === 0) return 'none'; - if (isGroupActive?.() ) return isInk() ? 'visiblePainted': (doc?. - isGroup )? undefined: 'all' + if (isGroupActive?.() ) return isInk() ? 'visiblePainted': (doc?.isGroup ) ? undefined: 'all'; if (isDocumentActive?.()) return isInk() ? 'visiblePainted' : 'all'; return undefined; // fixes problem with tree view elements getting pointer events when the tree view is not active case StyleProp.Decorations: { @@ -329,11 +325,12 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & // eslint-disable-next-line react/no-unstable-nested-components iconProvider={() => <div className='styleProvider-filterShift'><FaFilter/></div>} closeOnSelect - setSelectedVal={((dv: DocumentView) => { + setSelectedVal={((dvValue: unknown) => { + const dv = dvValue as DocumentView; dv.select(false); SnappingManager.SetPropertiesWidth(250); _filterOpener?.(); - }) as any // Dropdown assumes values are strings or numbers.. + }) // Dropdown assumes values are strings or numbers.. } size={Size.XSMALL} width={15} @@ -345,11 +342,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & background={showFilterIcon} items={[ ...(dashView ? [dashView]: []), ...(docViewPath?.()??[])] .filter(dv => StrListCast(dv?.Document.childFilters).length || StrListCast(dv?.Document.childRangeFilters).length) - .map(dv => ({ - text: StrCast(dv?.Document.title), - val: dv as any, - style: {color:SnappingManager.userColor, background:SnappingManager.userBackgroundColor}, - } as IListItemProps)) } + .map(dv => ({ text: StrCast(dv?.Document.title), + val: dv as unknown, + style: {color:SnappingManager.userColor, background:SnappingManager.userBackgroundColor} } as IListItemProps)) } /> </div> ); @@ -378,6 +373,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & } default: } + return undefined; } export function DashboardToggleButton(doc: Doc, field: string, onIcon: IconProp, offIcon: IconProp, clickFunc?: () => void) { @@ -386,7 +382,7 @@ export function DashboardToggleButton(doc: Doc, field: string, onIcon: IconProp, <IconButton size={Size.XSMALL} color={color} - icon={<FontAwesomeIcon icon={(doc[field] ? (onIcon as any) : offIcon) as IconProp} />} + icon={<FontAwesomeIcon icon={doc[field] ? onIcon : offIcon} />} onClick={undoBatch( action((e: React.MouseEvent) => { e.stopPropagation(); diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index 38f681e87..c799eb3c8 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -1,5 +1,3 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; @@ -15,7 +13,7 @@ import { StyleProp } from '../StyleProp'; import { DocumentView } from '../nodes/DocumentView'; import { FocusViewOptions } from '../nodes/FocusViewOptions'; import './CollectionCarousel3DView.scss'; -import { CollectionSubView } from './CollectionSubView'; +import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; // eslint-disable-next-line @typescript-eslint/no-var-requires const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = require('../global/globalCssVariables.module.scss'); @@ -25,7 +23,7 @@ export class CollectionCarousel3DView extends CollectionSubView() { @computed get scrollSpeed() { return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; // default scroll speed } - constructor(props: any) { + constructor(props: SubCollectionViewProps) { super(props); makeObservable(this); } @@ -181,8 +179,8 @@ export class CollectionCarousel3DView extends CollectionSubView() { className="collectionCarousel3DView-outer" ref={this.createDashEventsTarget} style={{ - background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor), - color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color), + background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string, + color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string, }}> <div className="carousel-wrapper" style={{ transform: `translateX(${this.translateX}px)` }}> {this.content} diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 2a36e96bf..5a142cc6e 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { action, IReactionDisposer, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -28,7 +29,7 @@ import { OverlayView } from '../OverlayView'; import { ScriptingRepl } from '../ScriptingRepl'; import { UndoStack } from '../UndoStack'; import './CollectionDockingView.scss'; -import { CollectionSubView } from './CollectionSubView'; +import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; const _global = (window /* browser */ || global) /* node */ as any; @@ -40,7 +41,7 @@ export class CollectionDockingView extends CollectionSubView() { * configuring golden layout to render its documents using the specified React component * @param ele - typically would be set to TabDocView */ - public static Init(ele: any) { + public static Init(ele: JSX.Element | null) { this.tabClass = ele; DocumentView.addSplit = CollectionDockingView.AddSplit; } @@ -60,7 +61,7 @@ export class CollectionDockingView extends CollectionSubView() { private _goldenLayout: any = null; static _highlightStyleSheet = addStyleSheet(); - constructor(props: any) { + constructor(props: SubCollectionViewProps) { super(props); makeObservable(this); if (this._props.renderDepth < 0) CollectionDockingView.Instance = this; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index e250d7a90..b7169ece0 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -67,7 +67,7 @@ export function CollectionSubView<X>() { private gestureDisposer?: GestureUtils.GestureEventDisposer; protected _mainCont?: HTMLDivElement; - constructor(props: any) { + constructor(props: X & SubCollectionViewProps) { super(props); makeObservable(this); } diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 46f61290e..f50f7394b 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -14,7 +14,7 @@ import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { FieldId } from '../../../fields/RefField'; import { ComputedField } from '../../../fields/ScriptField'; -import { Cast, DocCast, NumCast, StrCast, toList } from '../../../fields/Types'; +import { Cast, NumCast, StrCast, toList } from '../../../fields/Types'; import { DocServer } from '../../DocServer'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; @@ -67,7 +67,7 @@ class TabMiniThumb extends React.Component<TabMiniThumbProps> { } @observer export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps> { - static miniStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string): any => { + static miniStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => { if (doc) { switch (property.split(':')[0]) { case StyleProp.PointerEvents: return 'none'; @@ -274,7 +274,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { } static Activate = (tabDoc: Doc) => { - const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(findTab => findTab.DashDoc === tabDoc && !findTab.contentItem.config.props.keyValue); + const tab = Array.from(CollectionDockingView.Instance?.tabMap ?? []).find(findTab => findTab.DashDoc === tabDoc && !findTab.contentItem.config.props.keyValue); tab?.header.parent.setActiveContentItem(tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost) return tab !== undefined; }; @@ -286,7 +286,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { // } // return undefined; // } - constructor(props: any) { + constructor(props: TabDocViewProps) { super(props); makeObservable(this); DocumentView.activateTabView = TabDocView.Activate; @@ -399,9 +399,10 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { tab._disposers.color = reaction( () => ({ variant: SnappingManager.userVariantColor, degree: Doc.GetBrushStatus(doc), highlight: DefaultStyleProvider(this._document, undefined, StyleProp.Highlighting) }), ({ variant, degree, highlight }) => { - const color = highlight?.highlightIndex === Doc.DocBrushStatus.highlighted ? highlight.highlightColor : degree ? ['transparent', variant, variant, 'orange'][degree] : variant; + const { highlightIndex, highlightColor } = (highlight as { highlightIndex: number; highlightColor: string }) ?? { highlightIndex: undefined, highlightColor: undefined }; + const color = highlightIndex === Doc.DocBrushStatus.highlighted ? highlightColor : degree ? ['transparent', variant, variant, 'orange'][degree] : variant; - const textColor = color === variant ? SnappingManager.userColor ?? '' : lightOrDark(color); + const textColor = color === variant ? (SnappingManager.userColor ?? '') : lightOrDark(color); titleEle.style.color = textColor; iconWrap.style.color = textColor; closeWrap.style.color = textColor; diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index f69aea2a7..161d93788 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -472,7 +472,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> { refTransform = (ref: HTMLElement | undefined | null) => { if (!ref) return this.ScreenToLocalTransform(); const { translateX, translateY, scale } = ClientUtils.GetScreenTransform(ref); - return new Transform(-translateX, -translateY, 1).scale(1/scale); + return new Transform(-translateX, -translateY, 1).scale(1 / scale); }; docTransform = () => this.refTransform(this._dref?.ContentDiv); getTransform = () => this.refTransform(this._tref.current); @@ -777,7 +777,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> { @computed get renderBullet() { TraceMobx(); - const iconType = this.treeView._props.styleProvider?.(this.Document, this.treeView._props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ':treeOpen' : !this.childDocs.length ? ':empty' : '')) || 'question'; + const iconType = (this.treeView._props.styleProvider?.(this.Document, this.treeView._props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ':treeOpen' : !this.childDocs.length ? ':empty' : '')) as string) || 'question'; const color = SettingsManager.userColor; const checked = this.onCheckedClick ? this.Document.treeView_Checked ?? 'unchecked' : undefined; return ( @@ -923,7 +923,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> { style={{ // just render a title for a tree view label (identified by treeViewDoc being set in 'props') maxWidth: props?.PanelWidth() || undefined, - background: props?.styleProvider?.(doc, props, StyleProp.BackgroundColor), + background: props?.styleProvider?.(doc, props, StyleProp.BackgroundColor) as string, outline: SnappingManager.IsDragging ? undefined: `solid ${highlightColor} ${highlightIndex}px`, paddingLeft: NumCast(treeView.Document.childXPadding, NumCast(treeView._props.childXPadding, Doc.IsComicStyle(doc)?20:0)), paddingRight: NumCast(treeView.Document.childXPadding, NumCast(treeView._props.childXPadding, Doc.IsComicStyle(doc)?20:0)), @@ -1179,7 +1179,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> { @computed get renderBorder() { const sorting = StrCast(this.Document.treeView_SortCriterion, TreeSort.WhenAdded); - const sortings = (this._props.styleProvider?.(this.Document, this.treeView._props, StyleProp.TreeViewSortings) ?? {}) as { [key: string]: { color: string; label: string } }; + const sortings = (this._props.styleProvider?.(this.Document, this.treeView._props, StyleProp.TreeViewSortings) ?? {}) as { [key: string]: { color: string; icon: JSX.Element } }; return ( <div className={`treeView-border${this.treeView.outlineMode ? TreeViewType.outline : ''}`} style={{ borderColor: sortings[sorting]?.color }}> {!this.treeViewOpen ? null : this.renderContent} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index de51cc73c..79aad0ef2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -9,7 +9,7 @@ import { aggregateBounds } from '../../../../Utils'; export interface ViewDefBounds { type: string; - payload: any; + payload: unknown; x: number; y: number; z?: number; @@ -72,11 +72,15 @@ function toLabel(target: FieldResult<FieldType>) { */ function getTextWidth(text: string, font: string): number { // re-use canvas object for better performance - const canvas = (getTextWidth as any).canvas || ((getTextWidth as any).canvas = document.createElement('canvas')); + const selfStoreHack = getTextWidth as unknown as { canvas: Element }; + const canvas = (selfStoreHack.canvas = (selfStoreHack.canvas as unknown as HTMLCanvasElement) ?? document.createElement('canvas')); const context = canvas.getContext('2d'); - context.font = font; - const metrics = context.measureText(text); - return metrics.width; + if (context) { + context.font = font; + const metrics = context.measureText(text); + return metrics.width; + } + return 0; } interface PivotColumn { @@ -131,13 +135,13 @@ export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc return normalizeResults(burstDiam, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]); } -export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) { +export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: unknown) { const docMap = new Map<string, PoolData>(); const fieldKey = 'data'; const pivotColumnGroups = new Map<FieldResult<FieldType>, PivotColumn>(); let nonNumbers = 0; - const pivotFieldKey = toLabel(engineProps?.pivotField ?? pivotDoc._pivotField) || 'author'; + const pivotFieldKey = toLabel((engineProps as { pivotField?: string })?.pivotField ?? pivotDoc._pivotField) || 'author'; childPairs.forEach(pair => { const listValue = Cast(pair.layout[pivotFieldKey], listSpec('string'), null); @@ -265,7 +269,7 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight, - payload: pivotColumnGroups.get(key)!.filters, + payload: pivotColumnGroups.get(key)?.filters, })); groupNames.push(...dividers); // eslint-disable-next-line no-use-before-define diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 5b7f09be3..39c3da7a5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,6 +1,4 @@ /* eslint-disable react/jsx-props-no-spreading */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ -/* eslint-disable jsx-a11y/no-static-element-interactions */ import { Bezier } from 'bezier-js'; import { Colors } from 'browndash-components'; import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; @@ -9,7 +7,7 @@ import { computedFn } from 'mobx-utils'; import * as React from 'react'; import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils'; import { DateField } from '../../../../fields/DateField'; -import { Doc, DocListCast, Field, FieldType, Opt } from '../../../../fields/Doc'; +import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc'; import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveEraserWidth, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, SetActiveInkColor, SetActiveInkWidth } from '../../nodes/DocumentView'; import { DocData, Height, Width } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; @@ -46,7 +44,7 @@ import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; import { OpenWhere, OpenWhereMod } from '../../nodes/OpenWhere'; import { PinDocView, PinProps } from '../../PinFuncs'; import { StyleProp } from '../../StyleProp'; -import { CollectionSubView } from '../CollectionSubView'; +import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView'; import { TreeViewType } from '../CollectionTreeViewType'; import { CollectionFreeFormBackgroundGrid } from './CollectionFreeFormBackgroundGrid'; import { CollectionFreeFormClusters } from './CollectionFreeFormClusters'; @@ -71,7 +69,7 @@ export interface collectionFreeformViewProps { childPointerEvents?: () => string | undefined; viewField?: string; noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale) - engineProps?: any; + engineProps?: unknown; getScrollHeight?: () => number | undefined; } @@ -83,7 +81,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection public unprocessedDocs: Doc[] = []; public static collectionsWithUnprocessedInk = new Set<CollectionFreeFormView>(); public static from(dv?: DocumentView): CollectionFreeFormView | undefined { - const parent = CollectionFreeFormDocumentView.from(dv)?._props.parent; + const parent = CollectionFreeFormDocumentView.from(dv)?._props.reactParent; return parent instanceof CollectionFreeFormView ? parent : undefined; } @@ -123,14 +121,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @observable _marqueeViewRef = React.createRef<MarqueeView>(); @observable _brushedView: { width: number; height: number; panX: number; panY: number } | undefined = undefined; // highlighted region of freeform canvas used by presentations to indicate a region @observable GroupChildDrag: boolean = false; // child document view being dragged. needed to update drop areas of groups when a group item is dragged. - @observable _childPointerEvents: 'none' | 'all' | 'visiblepainted' | undefined = undefined; + @observable _childPointerEvents: 'none' | 'all' | 'visiblePainted' | undefined = undefined; @observable _lightboxDoc: Opt<Doc> = undefined; @observable _paintedId = 'id' + Utils.GenerateGuid().replace(/-/g, ''); @observable _keyframeEditing = false; @observable _eraserX: number = 0; @observable _eraserY: number = 0; @observable _showEraserCircle: boolean = false; // to determine whether the radius eraser should show - constructor(props: collectionFreeformViewProps) { + constructor(props: SubCollectionViewProps) { super(props); makeObservable(this); } @@ -140,12 +138,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @computed get childPointerEvents() { return SnappingManager.IsResizing ? 'none' - : this._props.childPointerEvents?.() ?? + : (this._props.childPointerEvents?.() ?? (this._props.viewDefDivClick || // (this.layoutEngine === computePassLayout.name && !this._props.isSelected()) || this.isContentActive() === false ? 'none' - : this._props.pointerEvents?.()); + : this._props.pointerEvents?.())); } @computed get contentViews() { const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask !== -1 && ele.inkMask !== undefined).map(ele => ele.ele); @@ -185,7 +183,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection .transform(this.panZoomXf); } @computed get backgroundColor() { - return this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor); + return this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) as string; } @computed get fitWidth() { return this._props.fitWidth?.(this.Document) ?? this.layoutDoc.layout_fitWidth; @@ -357,7 +355,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection * @param options * @returns */ - focus = (anchor: Doc, options: FocusViewOptions): any => { + focus = (anchor: Doc, options: FocusViewOptions) => { if (anchor.isGroup && !options.docTransform && options.contextPath?.length) { // don't focus on group if there's a context path because we're about to focus on a group item // which will override any group focus. (If we allowed the group to focus, it would mark didMove even if there were no net movement) @@ -374,14 +372,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const xfToCollection = options?.docTransform ?? Transform.Identity(); const savedState = { panX: NumCast(this.Document[this.panXFieldKey]), panY: NumCast(this.Document[this.panYFieldKey]), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined }; const cantTransform = this.fitContentsToBox || ((this.Document.isGroup || this.layoutDoc._lockedTransform) && !DocumentView.LightboxDoc()); - const { panX, panY, scale } = cantTransform || (!options.willPan && !options.willZoomCentered) ? savedState : this.calculatePanIntoView(anchor, xfToCollection, options?.willZoomCentered ? options?.zoomScale ?? 0.75 : undefined); + const { panX, panY, scale } = cantTransform || (!options.willPan && !options.willZoomCentered) ? savedState : this.calculatePanIntoView(anchor, xfToCollection, options?.willZoomCentered ? (options?.zoomScale ?? 0.75) : undefined); // focus on the document in the collection const didMove = !cantTransform && !anchor.z && (panX !== savedState.panX || panY !== savedState.panY || scale !== savedState.scale); if (didMove) options.didMove = true; // glr: freeform transform speed can be set by adjusting presentation_transition field - needs a way of knowing when presentation is not active... if (didMove) { - const focusTime = options?.instant ? 0 : options.zoomTime ?? 500; + const focusTime = options?.instant ? 0 : (options.zoomTime ?? 500); (options.zoomScale ?? options.willZoomCentered) && scale && (this.Document[this.scaleFieldKey] = scale); this.setPan(panX, panY, focusTime); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow return focusTime; @@ -1178,6 +1176,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection // for some reason bezier.js doesn't handle the case of intersecting a linear curve, so we wrap the intersection // call in a test for linearity bintersects = (curve: Bezier, otherCurve: Bezier) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any if ((curve as any)._linear) { // bezier.js doesn't intersect properly if the curve is actually a line -- so get intersect other curve against this line, then figure out the t coordinates of the intersection on this line const intersections = otherCurve.lineIntersects({ p1: curve.points[0], p2: curve.points[3] }); @@ -1187,6 +1186,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection return intT ? [intT] : []; } } + // eslint-disable-next-line @typescript-eslint/no-explicit-any if ((otherCurve as any)._linear) { return curve.lineIntersects({ p1: otherCurve.points[0], p2: otherCurve.points[3] }); } @@ -1478,7 +1478,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection return ret; }; childPointerEventsFunc = () => this._childPointerEvents; - childContentsActive = () => (this._props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)(); + childContentsActive = () => ((this._props.childContentsActive ?? this.isContentActive() === false) ? returnFalse : emptyFunction)(); getChildDocView(entry: PoolData) { const childLayout = entry.pair.layout; const childData = entry.pair.data; @@ -1488,7 +1488,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection {...OmitKeys(entry, ['replica', 'pair']).omit} key={childLayout[Id] + (entry.replica || '')} Document={childLayout} - parent={this} + reactParent={this} containerViewPath={this.DocumentView?.().docViewPath} styleProvider={this._clusters.styleProvider} TemplateDataDocument={childData} @@ -1603,7 +1603,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; } - onViewDefDivClick = (e: React.MouseEvent, payload: any) => { + onViewDefDivClick = (e: React.MouseEvent, payload: unknown) => { (this._props.viewDefDivClick || ScriptCast(this.Document.onViewDefDivClick))?.script.run({ this: this.Document, payload }); e.stopPropagation(); }; @@ -1637,7 +1637,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection ele: ( <div className="collectionFreeform-customDiv" - title={viewDef.payload?.join(' ')} + title={StrListCast(viewDef.payload as string).join(' ')} key={'div' + x + y + z + viewDef.payload} onClick={e => this.onViewDefDivClick(e, viewDef)} style={{ width, height, backgroundColor: color, transform }} @@ -1658,7 +1658,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection doEngineLayout( poolData: Map<string, PoolData>, - engine: (poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) => ViewDefResult[] + engine: (poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: unknown) => ViewDefResult[] ) { return engine(poolData, this.Document, this.childLayoutPairs, [this._props.PanelWidth(), this._props.PanelHeight()], this.viewDefsToJSX, this._props.engineProps); } @@ -1688,7 +1688,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection .forEach(entry => elements.push({ ele: this.getChildDocView(entry[1]), - bounds: (entry[1].opacity === 0 ? { payload:undefined, type:"", ...entry[1], width: 0, height: 0 } : { payload:undefined, type:"",...entry[1] }), + bounds: entry[1].opacity === 0 ? { payload: undefined, type: '', ...entry[1], width: 0, height: 0 } : { payload: undefined, type: '', ...entry[1] }, inkMask: BoolCast(entry[1].pair.layout.stroke_isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1, }) ); @@ -1771,7 +1771,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this._disposers.pointerevents = reaction( () => this.childPointerEvents, pointerevents => { - this._childPointerEvents = pointerevents as any; + this._childPointerEvents = pointerevents as 'none' | 'all' | 'visiblePainted' | undefined; }, { fireImmediately: true } ); @@ -1812,24 +1812,25 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection updateIcon = () => { const contentDiv = this.DocumentView?.().ContentDiv; - contentDiv && UpdateIcon( - this.layoutDoc[Id] + '-icon' + new Date().getTime(), - contentDiv, - NumCast(this.layoutDoc._width), - NumCast(this.layoutDoc._height), - this._props.PanelWidth(), - this._props.PanelHeight(), - 0, - 1, - false, - '', - (iconFile, nativeWidth, nativeHeight) => { - this.dataDoc.icon = new ImageField(iconFile); - this.dataDoc.icon_nativeWidth = nativeWidth; - this.dataDoc.icon_nativeHeight = nativeHeight; - } - ); - } + contentDiv && + UpdateIcon( + this.layoutDoc[Id] + '-icon' + new Date().getTime(), + contentDiv, + NumCast(this.layoutDoc._width), + NumCast(this.layoutDoc._height), + this._props.PanelWidth(), + this._props.PanelHeight(), + 0, + 1, + false, + '', + (iconFile, nativeWidth, nativeHeight) => { + this.dataDoc.icon = new ImageField(iconFile); + this.dataDoc.icon_nativeWidth = nativeWidth; + this.dataDoc.icon_nativeHeight = nativeHeight; + } + ); + }; @action onCursorMove = (e: React.PointerEvent) => { @@ -2134,7 +2135,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection onDragOver={e => e.preventDefault()} onContextMenu={this.onContextMenu} style={{ - pointerEvents: this._props.isContentActive() && SnappingManager.IsDragging ? 'all' : (this._props.pointerEvents?.() as any), + pointerEvents: this._props.isContentActive() && SnappingManager.IsDragging ? 'all' : this._props.pointerEvents?.(), textAlign: this.isAnnotationOverlay ? 'initial' : undefined, transform: `scale(${this.nativeDimScaling})`, width: `${100 / this.nativeDimScaling}%`, diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index ee67dd305..9a383c1e4 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -47,7 +47,7 @@ interface freeFormProps { export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { RenderCutoffProvider: (doc: Doc) => boolean; isAnyChildContentActive: () => boolean; - parent: any; + reactParent: React.Component; } @observer export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps & freeFormProps>() { @@ -71,7 +71,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF public static animStringFields = ['backgroundColor', '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 from(dv?: DocumentView): CollectionFreeFormDocumentView | undefined { - return dv?._props.parent instanceof CollectionFreeFormDocumentView ? dv._props.parent : undefined; + return dv?._props.reactParent instanceof CollectionFreeFormDocumentView ? dv._props.reactParent : undefined; } constructor(props: CollectionFreeFormDocumentViewProps & freeFormProps) { @@ -304,7 +304,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF <DocumentView // eslint-disable-next-line react/jsx-props-no-spreading {...OmitKeys(this._props,this.WrapperKeys.map(val => val.lower)).omit} // prettier-ignore - parent={this} + reactParent={this} DataTransition={this.DataTransition} LocalRotation={this.localRotation} CollectionFreeFormDocumentView={this.returnThis} 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} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 818d26956..fc59e9e26 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -4,7 +4,7 @@ import { computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { DateField } from '../../../fields/DateField'; -import { Doc, Field, Opt } from '../../../fields/Doc'; +import { Doc, Field, FieldType, Opt } from '../../../fields/Doc'; import { List } from '../../../fields/List'; import { ScriptField } from '../../../fields/ScriptField'; import { WebField } from '../../../fields/URLField'; @@ -18,7 +18,26 @@ import { OpenWhere } from './OpenWhere'; export type FocusFuncType = (doc: Doc, options: FocusViewOptions) => Opt<number>; // eslint-disable-next-line no-use-before-define -export type StyleProviderFuncType = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => any; +export type StyleProviderFuncType = ( + doc: Opt<Doc>, + props: Opt<FieldViewProps>, + property: string +) => + | Opt<FieldType> + | { clipPath: string; jsx: JSX.Element } + | JSX.Element + | JSX.IntrinsicElements + | null + | { + [key: string]: + | { + color: string; + icon: JSX.Element | string; + } + | undefined; + } + | { highlightStyle: string; highlightColor: string; highlightIndex: number; highlightStroke: boolean } + | undefined; // // these properties get assigned through the render() method of the DocumentView when it creates this node. // However, that only happens because the properties are "defined" in the markup for the field view. @@ -30,7 +49,7 @@ export interface FieldViewSharedProps { LayoutTemplateString?: string; LayoutTemplate?: () => Opt<Doc>; renderDepth: number; - scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document + scriptContext?: unknown; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document xPadding?: number; yPadding?: number; dontRegisterView?: boolean; @@ -45,7 +64,7 @@ export interface FieldViewSharedProps { containerViewPath?: () => DocumentView[]; fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document isGroupActive?: () => string | undefined; // is this document part of a group that is active - setContentViewBox?: (view: ViewBoxInterface<any>) => any; // called by rendered field's viewBox so that DocumentView can make direct calls to the viewBox + setContentViewBox?: (view: ViewBoxInterface<FieldViewProps>) => void; // called by rendered field's viewBox so that DocumentView can make direct calls to the viewBox PanelWidth: () => number; PanelHeight: () => number; isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events @@ -76,7 +95,7 @@ export interface FieldViewSharedProps { bringToFront?: (doc: Doc, sendToBack?: boolean) => void; waitForDoubleClickToClick?: () => 'never' | 'always' | undefined; defaultDoubleClick?: () => 'default' | 'ignore' | undefined; - pointerEvents?: () => Opt<string>; + pointerEvents?: () => Opt<'none' | 'all' | 'visiblePainted'>; suppressSetHeight?: boolean; } diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx index b8451fe60..37ffca2d6 100644 --- a/src/client/views/nodes/RecordingBox/RecordingView.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx @@ -1,6 +1,4 @@ -/* eslint-disable jsx-a11y/label-has-associated-control */ /* eslint-disable react/button-has-type */ -/* eslint-disable jsx-a11y/control-has-associated-label */ import * as React from 'react'; import { useEffect, useRef, useState } from 'react'; import { IconContext } from 'react-icons'; @@ -14,7 +12,7 @@ import { ProgressBar } from './ProgressBar'; import './RecordingView.scss'; export interface MediaSegment { - videoChunks: any[]; + videoChunks: Blob[]; endTime: number; startTime: number; presentation?: Presentation; @@ -91,15 +89,15 @@ export function RecordingView(props: IRecordingViewProps) { }, []); useEffect(() => { - let interval: any = null; + let interval: null | NodeJS.Timeout = null; if (recording) { interval = setInterval(() => { setRecordingTimer(unit => unit + 1); }, 10); } else if (!recording && recordingTimer !== 0) { - clearInterval(interval); + interval && clearInterval(interval); } - return () => clearInterval(interval); + return interval ? () => clearInterval(interval!) : undefined; }, [recording]); const setVideoProgressHelper = (curProgrss: number) => { @@ -127,9 +125,9 @@ export function RecordingView(props: IRecordingViewProps) { if (!videoRecorder.current) videoRecorder.current = new MediaRecorder(await startShowingStream()); // temporary chunks of video - let videoChunks: any = []; + let videoChunks: Blob[] = []; - videoRecorder.current.ondataavailable = (event: any) => { + videoRecorder.current.ondataavailable = (event: BlobEvent) => { if (event.data.size > 0) videoChunks.push(event.data); }; diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 3be50f5e6..9ef1071f7 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -1,4 +1,3 @@ -/* eslint-disable jsx-a11y/media-has-caption */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import * as React from 'react'; // import { Canvas } from '@react-three/fiber'; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 5b435e44a..b0c6120d4 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -14,7 +14,7 @@ import { EditorState, NodeSelection, Plugin, Selection, TextSelection, Transacti import { EditorView, NodeViewConstructor } from 'prosemirror-view'; import * as React from 'react'; import { BsMarkdownFill } from 'react-icons/bs'; -import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, StopEvent } from '../../../../ClientUtils'; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, returnFalse, returnZero, setupMoveUpEvents, simMouseEvent, smoothScroll, StopEvent } from '../../../../ClientUtils'; import { DateField } from '../../../../fields/DateField'; import { CreateLinkToActiveAudio, Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc'; import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, DocData, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols'; @@ -821,7 +821,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB let target = e.target as any; // hrefs are stored on the database of the <a> node that wraps the hyerlink <span> while (target && !target.dataset?.targethrefs) target = target.parentElement; const editor = this._editorView; - if (editor && target && !(e.nativeEvent as any).dash) { + if (editor && target && !(e.nativeEvent instanceof simMouseEvent ? e.nativeEvent.dash : false)) { const hrefs = (target.dataset?.targethrefs as string) ?.trim() .split(' ') |