diff options
author | bobzel <zzzman@gmail.com> | 2024-04-23 18:35:59 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2024-04-23 18:35:59 -0400 |
commit | 9d69ab27de83ead3e499edc9028ba85749407a1e (patch) | |
tree | fb7337fc899549665d6c7634435bc7ce518a58f9 /src | |
parent | 9e809f8748d1812bb03ec6471aa6f97467f8f75a (diff) |
more lint cleanup
Diffstat (limited to 'src')
39 files changed, 496 insertions, 402 deletions
diff --git a/src/client/goldenLayout.d.ts b/src/client/goldenLayout.d.ts index b50240563..26e8ba356 100644 --- a/src/client/goldenLayout.d.ts +++ b/src/client/goldenLayout.d.ts @@ -1,3 +1,2 @@ - declare const GoldenLayout: any; -export = GoldenLayout;
\ No newline at end of file +export = GoldenLayout; diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx index db7e64deb..b1eb730fa 100644 --- a/src/client/views/AntimodeMenu.tsx +++ b/src/client/views/AntimodeMenu.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; import { SettingsManager } from '../util/SettingsManager'; import './AntimodeMenu.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; + export interface AntimodeMenuProps {} /** @@ -76,7 +77,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends Observab }; @action - protected pointerLeave = (e: React.PointerEvent) => { + protected pointerLeave = () => { if (!this.Pinned && this._canFade) { this._transitionProperty = 'opacity'; this._transitionDuration = '0.5s'; @@ -87,7 +88,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends Observab }; @action - protected pointerEntered = (e: React.PointerEvent) => { + protected pointerEntered = () => { this._transitionProperty = 'opacity'; this._transitionDuration = '0.1s'; this._transitionDelay = ''; @@ -95,8 +96,10 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends Observab }; @action - protected togglePin = (e: React.MouseEvent) => { - runInAction(() => (this.Pinned = !this.Pinned)); + protected togglePin = () => { + runInAction(() => { + this.Pinned = !this.Pinned; + }); }; protected dragStart = (e: React.PointerEvent) => { @@ -114,8 +117,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends Observab @action protected dragging = (e: PointerEvent) => { - const width = this._mainCont.current!.getBoundingClientRect().width; - const height = this._mainCont.current!.getBoundingClientRect().height; + const { width, height } = this._mainCont.current!.getBoundingClientRect(); const left = e.pageX - this._offsetX; const top = e.pageY - this._offsetY; @@ -139,9 +141,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends Observab e.preventDefault(); }; - protected getDragger = () => { - return <div className="antimodeMenu-dragger" key="dragger" onPointerDown={this.dragStart} style={{ width: '20px' }} />; - }; + protected getDragger = () => <div className="antimodeMenu-dragger" key="dragger" onPointerDown={this.dragStart} style={{ width: '20px' }} />; protected getElement(buttons: JSX.Element, expanded: boolean = false) { const containerClass = expanded ? 'antimodeMenu-cont expanded' : 'antimodeMenu-cont'; diff --git a/src/client/views/ComponentDecorations.tsx b/src/client/views/ComponentDecorations.tsx index ca4e5f2bc..64b8a8446 100644 --- a/src/client/views/ComponentDecorations.tsx +++ b/src/client/views/ComponentDecorations.tsx @@ -1,10 +1,11 @@ import { observer } from 'mobx-react'; +import * as React from 'react'; import { SelectionManager } from '../util/SelectionManager'; import './ComponentDecorations.scss'; -import * as React from 'react'; @observer export class ComponentDecorations extends React.Component<{ boundsTop: number; boundsLeft: number }, { value: string }> { + // eslint-disable-next-line no-use-before-define static Instance: ComponentDecorations; render() { diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 18b433a77..d784a14b8 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -1,3 +1,6 @@ +/* eslint-disable react/no-array-index-key */ +/* eslint-disable react/jsx-props-no-spreading */ +/* eslint-disable default-param-last */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, IReactionDisposer, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; @@ -147,15 +150,13 @@ export class ContextMenu extends ObservableReactComponent<{}> { @computed get filteredItems(): (OriginalMenuProps | string[])[] { const searchString = this._searchString.toLowerCase().split(' '); - const matches = (descriptions: string[]): boolean => { - return searchString.every(s => descriptions.some(desc => desc.toLowerCase().includes(s))); - }; + const matches = (descriptions: string[]) => searchString.every(s => descriptions.some(desc => desc.toLowerCase().includes(s))); const flattenItems = (items: ContextMenuProps[], groupFunc: (groupName: any) => string[]) => { let eles: (OriginalMenuProps | string[])[] = []; const leaves: OriginalMenuProps[] = []; - for (const item of items) { - const description = item.description; + items.forEach(item => { + const { description } = item; const path = groupFunc(description); if ('subitems' in item) { const children = flattenItems(item.subitems, name => [...groupFunc(description), name]); @@ -163,13 +164,10 @@ export class ContextMenu extends ObservableReactComponent<{}> { eles.push(path); eles = eles.concat(children); } - } else { - if (!matches(path)) { - continue; - } + } else if (matches(path)) { leaves.push(item); } - } + }); eles = [...leaves, ...eles]; @@ -241,6 +239,7 @@ export class ContextMenu extends ObservableReactComponent<{}> { value={this._searchString} onKeyDown={this.onKeyDown} onChange={this.onChange} + // eslint-disable-next-line jsx-a11y/no-autofocus autoFocus /> </span> diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index fae3610ca..fd29b1ca4 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -249,7 +249,7 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() { removeDocument(doc: Doc | Doc[], annotationKey?: string, leavePushpin?: boolean, dontAddToRemoved?: boolean): boolean { const effectiveAcl = GetEffectiveAcl(this.dataDoc); const indocs = doc instanceof Doc ? [doc] : doc; - const docs = indocs.filter(doc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(doc) === AclAdmin); + const docs = indocs.filter(fdoc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(fdoc) === AclAdmin); // docs.forEach(doc => doc.annotationOn === this.Document && Doc.SetInPlace(doc, 'annotationOn', undefined, true)); const targetDataDoc = this.Document[DocData]; // this.dataDoc; // we want to write to the template, not the actual data doc @@ -258,13 +258,13 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() { if (toRemove.length !== 0) { const recentlyClosed = this.Document !== Doc.MyRecentlyClosed ? Doc.MyRecentlyClosed : undefined; - toRemove.forEach(doc => { + toRemove.forEach(rdoc => { // leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey); - Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc, true); - doc.embedContainer = undefined; - if (recentlyClosed && !dontAddToRemoved && doc.type !== DocumentType.LOADING) { - Doc.AddDocToList(recentlyClosed, 'data', doc, undefined, true, true); - Doc.RemoveEmbedding(doc, doc); + Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, rdoc, true); + rdoc.embedContainer = undefined; + if (recentlyClosed && !dontAddToRemoved && rdoc.type !== DocumentType.LOADING) { + Doc.AddDocToList(recentlyClosed, 'data', rdoc, undefined, true, true); + Doc.RemoveEmbedding(rdoc, rdoc); } }); if (targetDataDoc.isGroup && DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]).length < 2) { @@ -295,7 +295,7 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() { @action.bound addDocument = (doc: Doc | Doc[], annotationKey?: string): boolean => { const docs = doc instanceof Doc ? [doc] : doc; - if (this._props.filterAddDocument?.(docs) === false || docs.find(doc => Doc.AreProtosEqual(doc, this.Document) && Doc.LayoutField(doc) === Doc.LayoutField(this.Document))) { + if (this._props.filterAddDocument?.(docs) === false || docs.find(fdoc => Doc.AreProtosEqual(fdoc, this.Document) && Doc.LayoutField(fdoc) === Doc.LayoutField(this.Document))) { return false; } const targetDataDoc = this.Document[DocData]; // this.dataDoc; // we want to write to the template, not the actual data doc @@ -307,12 +307,12 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() { const added = docs; if (added.length) { if ([AclAugment, AclEdit, AclAdmin].includes(effectiveAcl)) { - added.forEach(doc => { - doc._dragOnlyWithinContainer = undefined; - if (annotationKey ?? this._annotationKeySuffix()) doc[DocData].annotationOn = this.Document; - else doc[DocData].annotationOn = undefined; - Doc.SetContainer(doc, this.Document); - inheritParentAcls(targetDataDoc, doc, true); + added.forEach(adoc => { + adoc._dragOnlyWithinContainer = undefined; + if (annotationKey ?? this._annotationKeySuffix()) adoc[DocData].annotationOn = this.Document; + else adoc[DocData].annotationOn = undefined; + Doc.SetContainer(adoc, this.Document); + inheritParentAcls(targetDataDoc, adoc, true); }); const annoDocs = Doc.Get(targetDataDoc, annotationKey ?? this.annotationKey, true) as List<Doc>; // get the dataDoc directly ... when using templates there may be some default items already there, but we can't change them, so we copy them below (should really be some kind of inheritance since the template contents could change) diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 63b485a43..c007af6fa 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -260,7 +260,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( const targetDoc = this.view0?.Document; return !targetDoc ? null : ( <Tooltip title={<div className="dash-tooltip">Open Context Menu</div>}> - <div className="documentButtonBar-icon" style={{ color: 'white', cursor: 'pointer' }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => this.openContextMenu(e))}> + <div className="documentButtonBar-icon" style={{ color: 'white', cursor: 'pointer' }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, clickEv => this.openContextMenu(clickEv))}> <FontAwesomeIcon className="documentdecorations-icon" icon="bars" /> </div> </Tooltip> diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 009097b3b..3083b9be0 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -154,7 +154,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora onContainerDown = (e: React.PointerEvent) => { const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views[0].Document); if (effectiveLayoutAcl === AclAdmin || effectiveLayoutAcl === AclEdit || effectiveLayoutAcl === AclAugment) { - setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), emptyFunction, emptyFunction); + setupMoveUpEvents(this, e, moveEv => this.onBackgroundMove(true, moveEv), emptyFunction, emptyFunction); e.stopPropagation(); } }; @@ -165,7 +165,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora setupMoveUpEvents( this, e, - e => this.onBackgroundMove(true, e), + moveEv => this.onBackgroundMove(true, moveEv), emptyFunction, action(() => { const selected = SelectionManager.Views.length === 1 ? SelectionManager.Docs[0] : undefined; @@ -179,7 +179,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora }; onBackgroundDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, e => this.onBackgroundMove(false, e), emptyFunction, emptyFunction); + setupMoveUpEvents(this, e, moveEv => this.onBackgroundMove(false, moveEv), emptyFunction, emptyFunction); e.stopPropagation(); }; @action @@ -308,10 +308,10 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora setupMoveUpEvents( this, e, - e => { + moveEv => { const [x, y] = [this.Bounds.x + 3, this.Bounds.y + 3]; const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2); - const dist = e.clientX < x && e.clientY < y ? 0 : Math.sqrt((e.clientX - x) * (e.clientX - x) + (e.clientY - y) * (e.clientY - y)); + const dist = moveEv.clientX < x && moveEv.clientY < y ? 0 : Math.sqrt((moveEv.clientX - x) * (moveEv.clientX - x) + (moveEv.clientY - y) * (moveEv.clientY - y)); SelectionManager.Docs.forEach(doc => { const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2); const radius = Math.min(1, dist / maxDist) * docMax; // set radius based on ratio of drag distance to half diagonal distance of bounding box @@ -358,7 +358,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora setupMoveUpEvents( this, e, - (e: PointerEvent, down: number[], delta: number[]) => // return false to keep getting events + (moveEv: PointerEvent, down: number[], delta: number[]) => // return false to keep getting events this.setRotateCenter(seldocview, [this.rotCenter[0] + delta[0], this.rotCenter[1] + delta[1]]) as any as boolean, action(() => { this._isRotating = false; }), // upEvent action(() => { seldocview.Document._rotation_centerX = seldocview.Document._rotation_centerY = 0; }), @@ -399,9 +399,9 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora setupMoveUpEvents( this, e, - (e: PointerEvent, down: number[], delta: number[]) => { - const previousPoint = { X: e.clientX, Y: e.clientY }; - const movedPoint = { X: e.clientX - delta[0], Y: e.clientY - delta[1] }; + (moveEv: PointerEvent, down: number[], delta: number[]) => { + const previousPoint = { X: moveEv.clientX, Y: moveEv.clientY }; + const movedPoint = { X: moveEv.clientX - delta[0], Y: moveEv.clientY - delta[1] }; const deltaAng = InkStrokeProperties.angleChange(movedPoint, previousPoint, rcScreen); if (selectedInk.length) { deltaAng && InkStrokeProperties.Instance.rotateInk(selectedInk, deltaAng, rcScreen); @@ -483,8 +483,8 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora this._interactionLock = true; this._snapPt = thisPt; e.ctrlKey && (SelectionManager.Views.forEach(docView => !Doc.NativeHeight(docView.Document) && docView.toggleNativeDimensions())); - const fixedAspect = SelectionManager.Docs.some(this.hasFixedAspect); - const scaleAspect = {x:scale.x === 1 && fixedAspect ? scale.y : scale.x, y: scale.x !== 1 && fixedAspect ? scale.x : scale.y}; + const hasFixedAspect = SelectionManager.Docs.some(this.hasFixedAspect); + const scaleAspect = {x:scale.x === 1 && hasFixedAspect ? scale.y : scale.x, y: scale.x !== 1 && hasFixedAspect ? scale.x : scale.y}; SelectionManager.Views.forEach(docView => this.resizeView(docView, refPt, scaleAspect, { dragHdl, ctrlKey:e.ctrlKey })); // prettier-ignore await new Promise<any>(res => { setTimeout(() => { res(this._interactionLock = undefined)})}); @@ -692,7 +692,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora }); const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => ( <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top"> - <div className={`documentDecorations-${key}Button`} onContextMenu={e => e.preventDefault()} onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => click!(e)))}> + <div className={`documentDecorations-${key}Button`} onContextMenu={e => e.preventDefault()} onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, clickEv => click!(clickEv)))}> <FontAwesomeIcon icon={icon as any} /> </div> </Tooltip> diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index be307600c..994107c01 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -301,7 +301,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> { } style={{ color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }} onBlur={undoable(e => Doc.setDocFilter(this.Document, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')} - onKeyDown={e => e.key === 'Enter' && undoable(e => Doc.setDocFilter(this.Document, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')(e)} + onKeyDown={e => e.key === 'Enter' && undoable(() => Doc.setDocFilter(this.Document, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')()} /> ); case 'checkbox': diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx index 0358344b9..b4fe44733 100644 --- a/src/client/views/InkControlPtHandles.tsx +++ b/src/client/views/InkControlPtHandles.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Doc } from '../../fields/Doc'; -import { ControlPoint, InkData, PointData } from '../../fields/InkField'; +import { ControlPoint, InkData } from '../../fields/InkField'; import { List } from '../../fields/List'; import { listSpec } from '../../fields/Schema'; import { Cast } from '../../fields/Types'; @@ -14,6 +14,7 @@ import { InkingStroke } from './InkingStroke'; import { InkStrokeProperties } from './InkStrokeProperties'; import { SnappingManager } from '../util/SnappingManager'; import { ObservableReactComponent } from './ObservableReactComponent'; +import { PointData } from '../../pen-gestures/GestureTypes'; export interface InkControlProps { inkDoc: Doc; @@ -48,7 +49,7 @@ export class InkControlPtHandles extends ObservableReactComponent<InkControlProp */ @action onControlDown = (e: React.PointerEvent, controlIndex: number): void => { - const ptFromScreen = this._props.inkView.ptFromScreen; + const { ptFromScreen } = this._props.inkView; if (ptFromScreen) { const order = controlIndex % 4; const handleIndexA = ((order === 3 ? controlIndex - 1 : controlIndex - 2) + this._props.inkCtrlPoints.length) % this._props.inkCtrlPoints.length; @@ -60,7 +61,7 @@ export class InkControlPtHandles extends ObservableReactComponent<InkControlProp setupMoveUpEvents( this, e, - action((e: PointerEvent, down: number[], delta: number[]) => { + action((moveEv: PointerEvent, down: number[], delta: number[]) => { if (!this._props.inkView.controlUndo) this._props.inkView.controlUndo = UndoManager.StartBatch('drag ink ctrl pt'); const inkMoveEnd = ptFromScreen({ X: delta[0], Y: delta[1] }); const inkMoveStart = ptFromScreen({ X: 0, Y: 0 }); @@ -75,9 +76,9 @@ export class InkControlPtHandles extends ObservableReactComponent<InkControlProp this._props.inkView.controlUndo = undefined; UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']); }), - action((e: PointerEvent, doubleTap: boolean | undefined) => { + action((moveEv: PointerEvent, doubleTap: boolean | undefined) => { const equivIndex = controlIndex === 0 ? this._props.inkCtrlPoints.length - 1 : controlIndex === this._props.inkCtrlPoints.length - 1 ? 0 : controlIndex; - if (doubleTap || e.button === 2) { + if (doubleTap || moveEv.button === 2) { if (!brokenIndices?.includes(equivIndex) && !brokenIndices?.includes(controlIndex)) { if (brokenIndices) brokenIndices.push(controlIndex); else this._props.inkDoc.brokenInkIndices = new List<number>([controlIndex]); @@ -128,7 +129,9 @@ export class InkControlPtHandles extends ObservableReactComponent<InkControlProp * Changes the current selected control point. */ @action - changeCurrPoint = (i: number) => (InkStrokeProperties.Instance._currentPoint = i); + changeCurrPoint = (i: number) => { + InkStrokeProperties.Instance._currentPoint = i; + }; render() { // Accessing the current ink's data and extracting all control points. @@ -176,7 +179,7 @@ export class InkControlPtHandles extends ObservableReactComponent<InkControlProp }; return ( <svg> - {!nearestScreenPt ? null : <circle key={'npt'} cx={nearestScreenPt.X} cy={nearestScreenPt.Y} r={this._props.screenSpaceLineWidth * 2} fill={'#00007777'} stroke={'#00007777'} strokeWidth={0} pointerEvents="none" />} + {!nearestScreenPt ? null : <circle key="npt" cx={nearestScreenPt.X} cy={nearestScreenPt.Y} r={this._props.screenSpaceLineWidth * 2} fill="#00007777" stroke="#00007777" strokeWidth={0} pointerEvents="none" />} {sreenCtrlPoints.map(control => hdl(control, this._overControl !== control.I ? 1 : 3 / 2, Colors.WHITE))} </svg> ); @@ -207,21 +210,21 @@ export class InkEndPtHandles extends ObservableReactComponent<InkEndProps> { setupMoveUpEvents( this, e, - action(e => { + action(moveEv => { if (this._throttle++ % 2 !== 0) return false; if (!this._props.inkView.controlUndo) this._props.inkView.controlUndo = UndoManager.StartBatch('stretch ink'); // compute stretch factor by finding scaling along axis between start and end points const p1 = pt1(); const p2 = pt2(); const v1 = { X: p1.X - p2.X, Y: p1.Y - p2.Y }; - const v2 = { X: e.clientX - p2.X, Y: e.clientY - p2.Y }; + const v2 = { X: moveEv.clientX - p2.X, Y: moveEv.clientY - p2.Y }; const v1len = Math.sqrt(v1.X * v1.X + v1.Y * v1.Y); const v2len = Math.sqrt(v2.X * v2.X + v2.Y * v2.Y); const scaling = v2len / v1len; const v1n = { X: v1.X / v1len, Y: v1.Y / v1len }; const v2n = { X: v2.X / v2len, Y: v2.Y / v2len }; const angle = Math.acos(v1n.X * v2n.X + v1n.Y * v2n.Y) * Math.sign(v1.X * v2.Y - v2.X * v1.Y); - InkStrokeProperties.Instance.stretchInk(SelectionManager.Views, scaling, p2, v1n, e.shiftKey); + InkStrokeProperties.Instance.stretchInk(SelectionManager.Views, scaling, p2, v1n, moveEv.shiftKey); InkStrokeProperties.Instance.rotateInk(SelectionManager.Views, angle, pt2()); // bcz: call pt2() func here because pt2 will have changed from previous stretchInk call return false; }), @@ -243,10 +246,14 @@ export class InkEndPtHandles extends ObservableReactComponent<InkEndProps> { cy={pt?.Y} r={this._props.screenSpaceLineWidth * 2} fill={this._overStart ? '#aaaaaa' : '#99999977'} - stroke={'#00007777'} + stroke="#00007777" strokeWidth={0} - onPointerLeave={action(() => (this._overStart = false))} - onPointerEnter={action(() => (this._overStart = true))} + onPointerLeave={action(() => { + this._overStart = false; + })} + onPointerEnter={action(() => { + this._overStart = true; + })} onPointerDown={dragFunc} pointerEvents="all" /> diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 52ea89cde..b3f9f9ea7 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -1,20 +1,22 @@ import { Bezier } from 'bezier-js'; +import * as _ from 'lodash'; import { action, makeObservable, observable, reaction, runInAction } from 'mobx'; import { Doc, NumListCast, Opt } from '../../fields/Doc'; -import { InkData, InkField, InkTool, PointData } from '../../fields/InkField'; +import { InkData, InkField, InkTool } from '../../fields/InkField'; import { List } from '../../fields/List'; import { listSpec } from '../../fields/Schema'; import { Cast, NumCast } from '../../fields/Types'; +import { PointData } from '../../pen-gestures/GestureTypes'; import { Point } from '../../pen-gestures/ndollar'; import { DocumentType } from '../documents/DocumentTypes'; -import { FitOneCurve } from '../util/bezierFit'; import { DocumentManager } from '../util/DocumentManager'; import { undoBatch } from '../util/UndoManager'; +import { FitOneCurve } from '../util/bezierFit'; import { InkingStroke } from './InkingStroke'; import { DocumentView } from './nodes/DocumentView'; -import * as _ from 'lodash'; export class InkStrokeProperties { + // eslint-disable-next-line no-use-before-define static _Instance: InkStrokeProperties | undefined; public static get Instance() { return this._Instance || new InkStrokeProperties(); @@ -28,11 +30,15 @@ export class InkStrokeProperties { makeObservable(this); reaction( () => this._controlButton, - button => button && (Doc.ActiveTool = InkTool.None) + button => { + button && (Doc.ActiveTool = InkTool.None); + } ); reaction( () => Doc.ActiveTool, - tool => tool !== InkTool.None && (this._controlButton = false) + tool => { + tool !== InkTool.None && (this._controlButton = false); + } ); } @@ -46,7 +52,7 @@ export class InkStrokeProperties { func: (view: DocumentView, ink: InkData, ptsXscale: number, ptsYscale: number, inkStrokeWidth: number) => { X: number; Y: number }[] | undefined, requireCurrPoint: boolean = false ) => { - var appliedFunc = false; + let appliedFunc = false; (strokes instanceof DocumentView ? [strokes] : strokes)?.forEach( action(inkView => { if (!requireCurrPoint || this._currentPoint !== -1) { @@ -85,7 +91,7 @@ export class InkStrokeProperties { */ @undoBatch addPoints = (inkView: DocumentView, t: number, i: number, controls: { X: number; Y: number }[]) => { - this.applyFunction(inkView, (view: DocumentView, ink: InkData) => { + this.applyFunction(inkView, (view: DocumentView /* , ink: InkData */) => { const doc = view.Document; const array = [controls[i], controls[i + 1], controls[i + 2], controls[i + 3]]; const newsegs = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).split(t); @@ -94,7 +100,9 @@ export class InkStrokeProperties { // Updating the indices of the control points whose handle tangency has been broken. doc.brokenInkIndices = new List(Cast(doc.brokenInkIndices, listSpec('number'), []).map(control => (control > i ? control + 4 : control))); - runInAction(() => (this._currentPoint = -1)); + runInAction(() => { + this._currentPoint = -1; + }); return controls; }); @@ -126,8 +134,8 @@ export class InkStrokeProperties { */ getNewHandlePoints = (C: PointData[], D: PointData[], newControl: PointData) => { const [m, n] = [C.length, D.length]; - let handleSizeA = Math.sqrt(Math.pow(newControl.X - C[0].X, 2) + Math.pow(newControl.Y - C[0].Y, 2)); - let handleSizeB = Math.sqrt(Math.pow(D[n - 1].X - newControl.X, 2) + Math.pow(D[n - 1].Y - newControl.Y, 2)); + let handleSizeA = Math.sqrt((newControl.X - C[0].X) ** 2 + (newControl.Y - C[0].Y) ** 2); + let handleSizeB = Math.sqrt((D[n - 1].X - newControl.X) ** 2 + (D[n - 1].Y - newControl.Y) ** 2); // Scaling adjustments to improve the ratio between the magnitudes of the two handle lines. // (Ensures that the new point added doesn't augment the inital shape of the curve much). if (handleSizeA < 75 && handleSizeB < 75) { @@ -167,13 +175,13 @@ export class InkStrokeProperties { const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4; const splicedPoints = ink.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8)); const samples: Point[] = []; - var startDir = { x: 0, y: 0 }; - var endDir = { x: 0, y: 0 }; - for (var i = 0; i < splicedPoints.length / 4; i++) { + let startDir = { x: 0, y: 0 }; + let endDir = { x: 0, y: 0 }; + for (let i = 0; i < splicedPoints.length / 4; i++) { const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y }))); if (i === 0) startDir = bez.derivative(0); if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(1); - for (var t = 0; t < (i === splicedPoints.length / 4 - 1 ? 1 + 1e-7 : 1); t += 0.05) { + for (let t = 0; t < (i === splicedPoints.length / 4 - 1 ? 1 + 1e-7 : 1); t += 0.05) { const pt = bez.compute(t); samples.push(new Point(pt.x, pt.y)); } @@ -186,7 +194,9 @@ export class InkStrokeProperties { } } doc.brokenInkIndices = new List(brokenIndices.map(control => (control >= this._currentPoint ? control - 4 : control))); - runInAction(() => (this._currentPoint = -1)); + runInAction(() => { + this._currentPoint = -1; + }); return newPoints.length < 4 ? undefined : newPoints; }, true @@ -200,7 +210,7 @@ export class InkStrokeProperties { */ @undoBatch rotateInk = (inkStrokes: DocumentView[], angle: number, scrpt: PointData) => { - this.applyFunction(inkStrokes, (view: DocumentView, ink: InkData, xScale: number, yScale: number, inkStrokeWidth: number) => { + this.applyFunction(inkStrokes, (view: DocumentView, ink: InkData, xScale: number, yScale: number /* , inkStrokeWidth: number */) => { const inkCenterPt = view.ComponentView?.ptFromScreen?.(scrpt); return !inkCenterPt ? ink @@ -247,8 +257,8 @@ export class InkStrokeProperties { const closed = InkingStroke.IsClosed(ink); const brokenIndices = Cast(inkView.Document.brokenInkIndices, listSpec('number'), []); if (origInk && this._currentPoint > 0 && this._currentPoint < ink.length - 1 && brokenIndices.findIndex(value => value === controlIndex) === -1) { - const cpt_before = ink[controlIndex]; - const cpt = { X: cpt_before.X + deltaX, Y: cpt_before.Y + deltaY }; + const cptBefore = ink[controlIndex]; + const cpt = { X: cptBefore.X + deltaX, Y: cptBefore.Y + deltaY }; const newink = origInk.slice(); const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4; const splicedPoints = origInk.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8)); @@ -256,28 +266,28 @@ export class InkStrokeProperties { if ((nearestSeg === 0 && nearestT < 1e-1) || (nearestSeg === 4 && 1 - nearestT < 1e-1)) return ink.slice(); const samplesLeft: Point[] = []; const samplesRight: Point[] = []; - var startDir = { x: 0, y: 0 }; - var endDir = { x: 0, y: 0 }; - for (var i = 0; i < nearestSeg / 4 + 1; i++) { + let startDir = { x: 0, y: 0 }; + let endDir = { x: 0, y: 0 }; + for (let i = 0; i < nearestSeg / 4 + 1; i++) { const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y }))); if (i === 0) startDir = bez.derivative(_.isEqual(bez.derivative(0), { x: 0, y: 0, t: 0 }) ? 1e-8 : 0); if (i === nearestSeg / 4) endDir = bez.derivative(nearestT); - for (var t = 0; t < (i === nearestSeg / 4 ? nearestT + 0.05 : 1); t += 0.05) { + for (let t = 0; t < (i === nearestSeg / 4 ? nearestT + 0.05 : 1); t += 0.05) { const pt = bez.compute(i !== nearestSeg / 4 ? t : Math.min(nearestT, t)); samplesLeft.push(new Point(pt.x, pt.y)); } } - var { finalCtrls } = FitOneCurve(samplesLeft, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y }); - for (var i = nearestSeg / 4; i < splicedPoints.length / 4; i++) { + let { finalCtrls } = FitOneCurve(samplesLeft, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y }); + for (let i = nearestSeg / 4; i < splicedPoints.length / 4; i++) { const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y }))); if (i === nearestSeg / 4) startDir = bez.derivative(nearestT); if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(_.isEqual(bez.derivative(1), { x: 0, y: 0, t: 1 }) ? 1 - 1e-8 : 1); - for (var t = i === nearestSeg / 4 ? nearestT : 0; t < (i === nearestSeg / 4 ? 1 + 0.05 + 1e-7 : 1 + 1e-7); t += 0.05) { + for (let t = i === nearestSeg / 4 ? nearestT : 0; t < (i === nearestSeg / 4 ? 1 + 0.05 + 1e-7 : 1 + 1e-7); t += 0.05) { const pt = bez.compute(Math.min(1, t)); samplesRight.push(new Point(pt.x, pt.y)); } } - const { finalCtrls: rightCtrls, error: errorRight } = FitOneCurve(samplesRight, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y }); + const { finalCtrls: rightCtrls /* , error: errorRight */ } = FitOneCurve(samplesRight, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y }); finalCtrls = finalCtrls.concat(rightCtrls); newink.splice(this._currentPoint - 4, 8, ...finalCtrls); return newink; @@ -307,11 +317,12 @@ export class InkStrokeProperties { }); public static nearestPtToStroke(ctrlPoints: { X: number; Y: number }[], refInkSpacePt: { X: number; Y: number }, excludeSegs?: number[]) { - var distance = Number.MAX_SAFE_INTEGER; - var nearestT = -1; - var nearestSeg = -1; - var nearestPt = { X: 0, Y: 0 }; - for (var i = 0; i < ctrlPoints.length - 3; i += 4) { + let distance = Number.MAX_SAFE_INTEGER; + let nearestT = -1; + let nearestSeg = -1; + let nearestPt = { X: 0, Y: 0 }; + for (let i = 0; i < ctrlPoints.length - 3; i += 4) { + // eslint-disable-next-line no-continue if (excludeSegs?.includes(i)) continue; const array = [ctrlPoints[i], ctrlPoints[i + 1], ctrlPoints[i + 2], ctrlPoints[i + 3]]; const point = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).project({ x: refInkSpacePt.X, y: refInkSpacePt.Y }); @@ -380,6 +391,7 @@ export class InkStrokeProperties { const snappedInkPt = doc === inkView.Document ? snapped.nearestPt : inkView.ComponentView?.ptFromScreen?.(testInkView?.ComponentView?.ptToScreen?.(snapped.nearestPt) ?? { X: 0, Y: 0 }); // convert from snapped ink coordinate system to dragged ink coordinate system by converting to/from screen space if (snappedInkPt) { + // eslint-disable-next-line no-param-reassign snapData = { nearestPt: snappedInkPt, distance: snapped.distance }; } } @@ -406,6 +418,7 @@ export class InkStrokeProperties { inkCopy[handleIndexB] = this.rotatePoint(handleB, controlPoint, angleDifference); return inkCopy; } + return undefined; }); }; @@ -430,7 +443,9 @@ export class InkStrokeProperties { const magnitudeB = Math.sqrt(vectorB.X * vectorB.X + vectorB.Y * vectorB.Y); if (magnitudeA === 0 || magnitudeB === 0) return 0; // Normalizing the vectors. + // eslint-disable-next-line no-param-reassign vectorA = { X: vectorA.X / magnitudeA, Y: vectorA.Y / magnitudeA }; + // eslint-disable-next-line no-param-reassign vectorB = { X: vectorB.X / magnitudeB, Y: vectorB.Y / magnitudeB }; return Math.acos(vectorB.X * vectorA.X + vectorB.Y * vectorA.Y); } diff --git a/src/client/views/InkTangentHandles.tsx b/src/client/views/InkTangentHandles.tsx index f3a757f19..577acc4d1 100644 --- a/src/client/views/InkTangentHandles.tsx +++ b/src/client/views/InkTangentHandles.tsx @@ -8,17 +8,16 @@ import { listSpec } from '../../fields/Schema'; import { Cast } from '../../fields/Types'; import { emptyFunction } from '../../Utils'; import { setupMoveUpEvents } from '../../ClientUtils'; -import { Transform } from '../util/Transform'; import { UndoManager } from '../util/UndoManager'; import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; import { InkStrokeProperties } from './InkStrokeProperties'; + export interface InkHandlesProps { inkDoc: Doc; inkView: InkingStroke; screenCtrlPoints: InkData; screenSpaceLineWidth: number; - ScreenToLocalTransform: () => Transform; } @observer @@ -38,9 +37,9 @@ export class InkTangentHandles extends React.Component<InkHandlesProps> { setupMoveUpEvents( this, e, - action((e: PointerEvent, down: number[], delta: number[]) => { + action((moveEv: PointerEvent, down: number[], delta: number[]) => { if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('DocDecs move tangent'); - if (e.altKey) this.onBreakTangent(controlIndex); + if (moveEv.altKey) this.onBreakTangent(controlIndex); const inkMoveEnd = this.props.inkView.ptFromScreen({ X: delta[0], Y: delta[1] }); const inkMoveStart = this.props.inkView.ptFromScreen({ X: 0, Y: 0 }); this.docView && InkStrokeProperties.Instance.moveTangentHandle(this.docView, -(inkMoveEnd.X - inkMoveStart.X), -(inkMoveEnd.Y - inkMoveStart.Y), handleIndex, oppositeHandleIndex, controlIndex); @@ -101,11 +100,12 @@ export class InkTangentHandles extends React.Component<InkHandlesProps> { tangentLines.push({ X1: data[i].X, Y1: data[i].Y, X2: data[i + 1].X, Y2: data[i + 1].Y, X3: data[i + 3].X, Y3: data[i + 3].Y, dot1: i + 1, dot2: i + 2 }); } } - const screenSpaceLineWidth = this.props.screenSpaceLineWidth; + const { screenSpaceLineWidth } = this.props; return ( <> {tangentHandles.map((pts, i) => ( + // eslint-disable-next-line react/no-array-index-key <svg height="10" width="10" key={`hdl${i}`}> <circle cx={pts.X} @@ -129,12 +129,13 @@ export class InkTangentHandles extends React.Component<InkHandlesProps> { x2={x2} y2={y2} stroke={Colors.MEDIUM_BLUE} - strokeDasharray={'1 1'} + strokeDasharray="1 1" strokeWidth={1} display={pts.dot1 === InkStrokeProperties.Instance._currentPoint || pts.dot2 === InkStrokeProperties.Instance._currentPoint ? 'inherit' : 'none'} /> ); return ( + // eslint-disable-next-line react/no-array-index-key <svg height="100" width="100" key={`line${i}`}> {tangentLine(pts.X1, pts.Y1, pts.X2, pts.Y2)} {tangentLine(pts.X2, pts.Y2, pts.X3, pts.Y3)} diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index e04daec3b..35067047b 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -144,7 +144,7 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>() e, !isEditing ? returnFalse - : action((e: PointerEvent, down: number[], delta: number[]) => { + : action((moveEv: PointerEvent, down: number[], delta: number[]) => { if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch('drag ink ctrl pt'); const inkMoveEnd = this.ptFromScreen({ X: delta[0], Y: delta[1] }); const inkMoveStart = this.ptFromScreen({ X: 0, Y: 0 }); @@ -159,7 +159,7 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>() this.controlUndo = undefined; UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']); }), - action((e: PointerEvent, doubleTap: boolean | undefined) => { + action((moveEv: PointerEvent, doubleTap: boolean | undefined) => { if (doubleTap) { InkStrokeProperties.Instance._controlButton = true; InkStrokeProperties.Instance._currentPoint = -1; @@ -331,7 +331,7 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>() false )} <InkControlPtHandles inkView={this} inkDoc={inkDoc} inkCtrlPoints={inkData} screenCtrlPoints={this.screenCtrlPts} nearestScreenPt={this.nearestScreenPt} screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} /> - <InkTangentHandles inkView={this} inkDoc={inkDoc} screenCtrlPoints={this.screenCtrlPts} screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} ScreenToLocalTransform={this.ScreenToLocalBoxXf} /> + <InkTangentHandles inkView={this} inkDoc={inkDoc} screenCtrlPoints={this.screenCtrlPts} screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} /> </div> ); }; diff --git a/src/client/views/KeyphraseQueryView.tsx b/src/client/views/KeyphraseQueryView.tsx index e996fc946..81f004010 100644 --- a/src/client/views/KeyphraseQueryView.tsx +++ b/src/client/views/KeyphraseQueryView.tsx @@ -1,3 +1,4 @@ +/* eslint-disable jsx-a11y/label-has-associated-control */ import { observer } from 'mobx-react'; import * as React from 'react'; import './KeyphraseQueryView.scss'; @@ -9,28 +10,21 @@ export interface KP_Props { @observer export class KeyphraseQueryView extends React.Component<KP_Props> { - constructor(props: KP_Props) { - super(props); - } - render() { - const kps = this.props.keyphrases.toString(); const keyterms = this.props.keyphrases.split(','); return ( <div> <h5>Select queries to send:</h5> <form> - {keyterms.map((kp: string) => { - //return (<p>{"-" + kp}</p>); - return ( - <p> - <label> - <input name="query" type="radio" /> - <span>{kp}</span> - </label> - </p> - ); - })} + {keyterms.map((kp: string) => ( + // return (<p>{"-" + kp}</p>); + <p> + <label> + <input name="query" type="radio" /> + <span>{kp}</span> + </label> + </p> + ))} </form> </div> ); diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index c34554b56..24433cd01 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -1,3 +1,6 @@ +/* eslint-disable no-use-before-define */ +/* 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 { Toggle, ToggleType, Type } from 'browndash-components'; @@ -43,6 +46,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { */ public static Contains(view?:DocumentView) { return view && LightboxView.Instance?._docView && (view.containerViewPath?.() ?? []).concat(view).includes(LightboxView.Instance?._docView); } // prettier-ignore public static get LightboxDoc() { return LightboxView.Instance?._doc; } // prettier-ignore + // eslint-disable-next-line no-use-before-define static Instance: LightboxView; private _path: { doc: Opt<Doc>; // @@ -72,11 +76,18 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { @action public SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc | string) { const lightDoc = this._doc; - lightDoc && lightDoc !== doc && savedKeys.forEach(key => (lightDoc[key] = this._savedState[key])); + lightDoc && + lightDoc !== doc && + savedKeys.forEach(key => { + lightDoc[key] = this._savedState[key]; + }); this._savedState = {}; if (doc) { - lightDoc !== doc && savedKeys.map(key => (this._savedState[key] = Doc.Get(doc, key, true))); + lightDoc !== doc && + savedKeys.forEach(key => { + this._savedState[key] = Doc.Get(doc, key, true); + }); const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); @@ -107,8 +118,9 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { return true; } - public AddDocTab = (doc: Doc, location: OpenWhere, layoutTemplate?: Doc | string) => - this.SetLightboxDoc( + public AddDocTab = (docs: Doc | Doc[], location: OpenWhere, layoutTemplate?: Doc | string) => { + const doc = (docs instanceof Doc ? [docs] : docs).lastElement(); + return this.SetLightboxDoc( doc, undefined, [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...this._future].sort( @@ -116,6 +128,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { ), layoutTemplate ); + }; @action next = () => { const lightDoc = this._doc; @@ -180,8 +193,12 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { this.SetLightboxDoc(undefined); } }; - toggleFitWidth = () => this._doc && (this._doc._layout_fitWidth = !this._doc._layout_fitWidth); - togglePen = () => (Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen); + toggleFitWidth = () => { + this._doc && (this._doc._layout_fitWidth = !this._doc._layout_fitWidth); + }; + togglePen = () => { + Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen; + }; toggleExplore = () => SnappingManager.SetExploreMode(!SnappingManager.ExploreMode); lightboxDoc = () => this._doc; @@ -191,33 +208,31 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { lightboxDocTemplate = () => this._layoutTemplate; future = () => this._future; - renderNavBtn = (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: IconProp, display: any, click: () => void, color?: string) => { - return ( + renderNavBtn = (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: IconProp, display: any, click: () => void, color?: string) => ( + <div + className="lightboxView-navBtn-frame" + style={{ + display: display ? '' : 'none', + left, + width: bottom !== undefined ? undefined : Math.min(this._props.PanelWidth / 4, this._props.maxBorder[0]), + bottom, + }}> <div - className="lightboxView-navBtn-frame" - style={{ - display: display ? '' : 'none', - left, - width: bottom !== undefined ? undefined : Math.min(this._props.PanelWidth / 4, this._props.maxBorder[0]), - bottom, + className="lightboxView-navBtn" + title={color} + style={{ top, color: SettingsManager.userColor, background: undefined }} + onClick={e => { + e.stopPropagation(); + click(); }}> - <div - className="lightboxView-navBtn" - title={color} - style={{ top, color: SettingsManager.userColor, background: undefined }} - onClick={e => { - e.stopPropagation(); - click(); - }}> - <div style={{ height: 10 }}>{color}</div> - <FontAwesomeIcon icon={icon} size="3x" /> - </div> + <div style={{ height: 10 }}>{color}</div> + <FontAwesomeIcon icon={icon} size="3x" /> </div> - ); - }; + </div> + ); render() { - let downx = 0, - downy = 0; + let downx = 0; + let downy = 0; const toggleBtn = (classname: string, tooltip: string, toggleBackground: any, icon: IconProp, icon2: IconProp | string, onClick: () => void) => ( <div className={classname}> <Toggle @@ -253,10 +268,12 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { clipPath: `path('${Doc.UserDoc().renderStyle === 'comic' ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`, background: SettingsManager.userBackgroundColor, }}> - <GestureOverlay isActive={true}> + <GestureOverlay isActive> <DocumentView key={this._doc.title + this._doc[Id]} // this makes a new DocumentView when the document changes which makes link following work, otherwise no DocView is registered for the new Doc - ref={action((r: DocumentView | null) => (this._docView = r !== null ? r : undefined))} + ref={action((r: DocumentView | null) => { + this._docView = r !== null ? r : undefined; + })} Document={this._doc} PanelWidth={this.lightboxWidth} PanelHeight={this.lightboxHeight} @@ -266,7 +283,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { styleProvider={DefaultStyleProvider} ScreenToLocalTransform={this.lightboxScreenToLocal} renderDepth={0} - suppressSetHeight={this._doc._layout_fitWidth ? true : false} + suppressSetHeight={!!this._doc._layout_fitWidth} containerViewPath={returnEmptyDoclist} childFilters={returnEmptyFilter} childFiltersByRanges={returnEmptyFilter} @@ -303,6 +320,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> { } interface LightboxTourBtnProps { navBtn: (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: IconProp, display: any, click: () => void, color?: string) => JSX.Element; + // eslint-disable-next-line react/no-unused-prop-types future: () => Opt<Doc[]>; stepInto: () => void; lightboxDoc: () => Opt<Doc>; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 17c21326d..01f3b032e 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-new */ // if ((module as any).hot) { // (module as any).hot.accept(); // } @@ -5,7 +6,7 @@ import * as dotenv from 'dotenv'; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import import * as React from 'react'; import * as ReactDOM from 'react-dom/client'; -import { AssignAllExtensions } from '../../extensions/General/Extensions'; +import { AssignAllExtensions } from '../../extensions/Extensions'; import { FieldLoader } from '../../fields/FieldLoader'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; import { PingManager } from '../util/PingManager'; @@ -15,6 +16,7 @@ import { CollectionView } from './collections/CollectionView'; import './global/globalScripts'; import { MainView } from './MainView'; import { BranchingTrailManager } from '../util/BranchingTrailManager'; + dotenv.config(); AssignAllExtensions(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index a13fe1cb7..10d423c05 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -747,7 +747,8 @@ export class MainView extends ObservableReactComponent<{}> { sidebarScreenToLocal = () => new Transform(0, -this.topOfSidebarDoc, 1); mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0); - static addDocTabFunc_impl = (doc: Doc, location: OpenWhere): boolean => { + static addDocTabFunc_impl = (docs: Doc | Doc[], location: OpenWhere): boolean => { + const doc = (docs instanceof Doc ? [docs] : docs).lastElement(); const whereFields = location.split(':'); const keyValue = whereFields.includes(OpenWhereMod.keyvalue); const whereMods = whereFields.length > 1 ? (whereFields[1] as OpenWhereMod) : OpenWhereMod.none; @@ -957,7 +958,7 @@ export class MainView extends ObservableReactComponent<{}> { childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} /> - {['watching', 'recording'].includes(StrCast(this.userDoc?.presentationMode)) ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{StrCast(this.userDoc?.presentationMode)}</div> : <></>} + {['watching', 'recording'].includes(StrCast(this.userDoc?.presentationMode)) ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{StrCast(this.userDoc?.presentationMode)}</div> : null} </div> ); } diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx index e52562625..a6dc5c62b 100644 --- a/src/client/views/MainViewModal.tsx +++ b/src/client/views/MainViewModal.tsx @@ -1,3 +1,6 @@ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable react/require-default-props */ import { isDark } from 'browndash-components'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -11,7 +14,6 @@ export interface MainViewOverlayProps { dialogueBoxStyle?: React.CSSProperties; overlayStyle?: React.CSSProperties; dialogueBoxDisplayedOpacity?: number; - overlayDisplayedOpacity?: number; closeOnExternalClick?: () => void; // the close method of a MainViewModal, triggered if there is a click on the overlay (closing the modal) } @@ -20,7 +22,6 @@ export class MainViewModal extends React.Component<MainViewOverlayProps> { render() { const p = this.props; const dialogueOpacity = p.dialogueBoxDisplayedOpacity || 1; - const overlayOpacity = p.overlayDisplayedOpacity || 0.4; return !p.isDisplayed ? null : ( <div className="mainViewModal-cont" diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index f92bd68a3..0301632ec 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -16,7 +16,6 @@ import { DocumentView } from './nodes/DocumentView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { ObservableReactComponent } from './ObservableReactComponent'; import { AnchorMenu } from './pdf/AnchorMenu'; -const _global = (window /* browser */ || global) /* node */ as any; export interface MarqueeAnnotatorProps { Document: Doc; @@ -101,8 +100,8 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP let minY = Number.MAX_VALUE; let maxY = -Number.MIN_VALUE; const annoDocs: Doc[] = []; - savedAnnoMap.forEach((value: HTMLDivElement[], key: number) => - value.map(anno => { + savedAnnoMap.forEach((value: HTMLDivElement[]) => + value.forEach(anno => { const textRegion = new Doc(); textRegion.x = parseInt(anno.style.left ?? '0'); textRegion.y = parseInt(anno.style.top ?? '0'); @@ -131,7 +130,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP return textRegionAnno; }; @action - highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean, summarize?: boolean) => { + highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => { // creates annotation documents for current highlights const effectiveAcl = GetEffectiveAcl(this.props.Document[DocData]); const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations); @@ -157,7 +156,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP // 3) localize the unrotated vector by scaling into the marquee container's coordinates // 4) reattach the vector to the center of the bounding box getTransformedScreenPt = (down: number[]) => { - const marqueeContainer = this.props.marqueeContainer; + const { marqueeContainer } = this.props; const containerXf = this.props.isNativeScaled ? this.props.docView().screenToContentsTransform() : this.props.docView().screenToViewTransform(); const boundingRect = marqueeContainer.getBoundingClientRect(); const center = { x: boundingRect.x + boundingRect.width / 2, y: boundingRect.y + boundingRect.height / 2 }; @@ -177,15 +176,15 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP document.addEventListener('pointermove', this.onSelectMove); document.addEventListener('pointerup', this.onSelectEnd); - AnchorMenu.Instance.OnCrop = (e: PointerEvent) => { + AnchorMenu.Instance.OnCrop = () => { if (this.props.anchorMenuCrop) { UndoManager.RunInBatch(() => this.props.anchorMenuCrop?.(this.highlight('', true, undefined, false), true), 'cropping'); } }; - AnchorMenu.Instance.OnClick = undoable((e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true)), 'make sidebar annotation'); + AnchorMenu.Instance.OnClick = undoable(() => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true)), 'make sidebar annotation'); AnchorMenu.Instance.OnAudio = unimplementedFunction; AnchorMenu.Instance.Highlight = (color: string) => this.highlight(color, false, undefined, true); - AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations, true); + AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]> /* , addAsAnnotation?: boolean */) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations, true); AnchorMenu.Instance.onMakeAnchor = () => AnchorMenu.Instance.GetAnchor(undefined, true); /** @@ -220,13 +219,14 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP : action((e: PointerEvent, ele: HTMLElement) => { e.preventDefault(); e.stopPropagation(); - var cropRegion: Doc | undefined; + let cropRegion: Doc | undefined; + // eslint-disable-next-line no-return-assign const sourceAnchorCreator = () => (cropRegion = this.highlight('', true, undefined, true)); // hyperlink color - const targetCreator = (annotationOn: Doc | undefined) => this.props.anchorMenuCrop!(cropRegion, false)!; + const targetCreator = (/* annotationOn: Doc | undefined */) => this.props.anchorMenuCrop!(cropRegion, false)!; DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView(), sourceAnchorCreator, targetCreator), e.pageX, e.pageY, { - dragComplete: e => { - if (!e.aborted && e.linkDocument) { - const linkDocData = e.linkDocument[DocData]; + dragComplete: dragEx => { + if (!dragEx.aborted && dragEx.linkDocument) { + const linkDocData = dragEx.linkDocument[DocData]; linkDocData.link_relationship = 'cropped image'; linkDocData.title = 'crop: ' + this.props.Document.title; linkDocData.link_displayLine = false; @@ -252,7 +252,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP const movLoc = this.getTransformedScreenPt([e.clientX, e.clientY]); this._width = movLoc.x - this._start.x; this._height = movLoc.y - this._start.y; - //e.stopPropagation(); // overlay documents are all 'active', yet they can be dragged. if we stop propagation, then they can be marqueed but not dragged. if we don't stop, then they will be marqueed and dragged, but the marquee will be zero width since the doc will move along with the cursor. + // e.stopPropagation(); // overlay documents are all 'active', yet they can be dragged. if we stop propagation, then they can be marqueed but not dragged. if we don't stop, then they will be marqueed and dragged, but the marquee will be zero width since the doc will move along with the cursor. }; onSelectEnd = (e: PointerEvent) => { @@ -268,7 +268,9 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP // copy the temporary marquee to allow for multiple selections (not currently available though). const copy = document.createElement('div'); const scale = (this.props.scaling?.() || 1) * NumCast(this.props.Document._freeform_scale, 1); - ['border', 'opacity', 'top', 'left', 'width', 'height'].forEach(prop => (copy.style[prop as any] = marqueeStyle[prop as any])); + ['border', 'opacity', 'top', 'left', 'width', 'height'].forEach(prop => { + copy.style[prop as any] = marqueeStyle[prop as any]; + }); copy.className = 'marqueeAnnotator-annotationBox'; copy.style.top = parseInt(marqueeStyle.top.toString().replace('px', '')) / scale + this.props.scrollTop + 'px'; copy.style.left = parseInt(marqueeStyle.left.toString().replace('px', '')) / scale + 'px'; diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 6d03034ec..fddb40624 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -13,6 +13,7 @@ import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; @observer export class PreviewCursor extends ObservableReactComponent<{}> { + // eslint-disable-next-line no-use-before-define static _instance: PreviewCursor; public static get Instance() { return PreviewCursor._instance; @@ -39,7 +40,9 @@ export class PreviewCursor extends ObservableReactComponent<{}> { paste = async (e: ClipboardEvent) => { if (this.Visible && e.clipboardData) { const newPoint = this._getTransform?.().transformPoint(this._clickPoint[0], this._clickPoint[1]); - runInAction(() => (this.Visible = false)); + runInAction(() => { + this.Visible = false; + }); // tests for URL and makes web document const re: any = /^https?:\/\//g; @@ -88,10 +91,10 @@ export class PreviewCursor extends ObservableReactComponent<{}> { UndoManager.RunInBatch(() => this._addLiveTextDoc?.(DocUtils.GetNewTextDoc('', newPoint[0], newPoint[1], 500, undefined, undefined)), 'paste'); } } - //pasting in images + // pasting in images else if (e.clipboardData.getData('text/html') !== '' && e.clipboardData.getData('text/html').includes('<img src=')) { - const re: any = /<img src="(.*?)"/g; - const arr: any[] = re.exec(e.clipboardData.getData('text/html')); + const regEx: any = /<img src="(.*?)"/g; + const arr: any[] = regEx.exec(e.clipboardData.getData('text/html')); if (newPoint) { undoBatch(() => { @@ -122,7 +125,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> { @action onKeyDown = (e: KeyboardEvent) => { // Mixing events between React and Native is finicky. - //if not these keys, make a textbox if preview cursor is active! + // if not these keys, make a textbox if preview cursor is active! if ( e.key !== 'Escape' && e.key !== 'Backspace' && @@ -162,7 +165,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> { } }; - //when focus is lost, this will remove the preview cursor + // when focus is lost, this will remove the preview cursor @action onBlur = (): void => { this.Visible = false; }; @@ -192,6 +195,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> { } render() { return !this._clickPoint || !this.Visible ? null : ( + // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex <div className="previewCursor" onBlur={this.onBlur} tabIndex={0} ref={e => e?.focus()} style={{ color: lightOrDark(this.Doc?.backgroundColor ?? 'white'), transform: `translate(${this._clickPoint[0]}px, ${this._clickPoint[1]}px)` }}> I </div> diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx index cf5105efc..be5b7ec1f 100644 --- a/src/client/views/PropertiesDocBacklinksSelector.tsx +++ b/src/client/views/PropertiesDocBacklinksSelector.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/no-unused-prop-types */ import { action } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx index b8bbde9de..2d04f2fe3 100644 --- a/src/client/views/PropertiesDocContextSelector.tsx +++ b/src/client/views/PropertiesDocContextSelector.tsx @@ -1,7 +1,10 @@ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable jsx-a11y/anchor-is-valid */ import { computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, DocListCast } from '../../fields/Doc'; +import { Doc } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { Cast, StrCast } from '../../fields/Types'; import { DocFocusOrOpen } from '../util/DocumentManager'; @@ -52,9 +55,9 @@ export class PropertiesDocContextSelector extends ObservableReactComponent<Prope .map(doc => ({ col: doc, target })); } - getOnClick = (col: Doc, target: Doc) => { + getOnClick = (clickCol: Doc) => { if (!this._props.DocView) return; - col = Doc.IsDataProto(col) ? Doc.MakeDelegate(col) : col; + const col = Doc.IsDataProto(clickCol) ? Doc.MakeDelegate(clickCol) : clickCol; DocFocusOrOpen(Doc.GetProto(this._props.DocView.Document), undefined, col); }; @@ -65,7 +68,7 @@ export class PropertiesDocContextSelector extends ObservableReactComponent<Prope {this._props.hideTitle ? null : <p key="contexts">Contexts:</p>} {this._docs.map(doc => ( <p key={doc.col[Id] + doc.target[Id]}> - <a onClick={() => this.getOnClick(doc.col, doc.target)}>{StrCast(doc.col.title)}</a> + <a onClick={() => this.getOnClick(doc.col)}>{StrCast(doc.col.title)}</a> </p> ))} </div> diff --git a/src/client/views/PropertiesSection.tsx b/src/client/views/PropertiesSection.tsx index 3c9fa1123..f083f03b9 100644 --- a/src/client/views/PropertiesSection.tsx +++ b/src/client/views/PropertiesSection.tsx @@ -1,5 +1,8 @@ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable react/require-default-props */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable } from 'mobx'; +import { action, computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { SettingsManager } from '../util/SettingsManager'; @@ -10,7 +13,6 @@ export interface PropertiesSectionProps { children?: JSX.Element | string | null; isOpen: boolean; setIsOpen: (bool: boolean) => any; - inSection?: boolean; setInSection?: (bool: boolean) => any; onDoubleClick?: () => void; } @@ -21,44 +23,35 @@ export class PropertiesSection extends React.Component<PropertiesSectionProps> { return SettingsManager.userColor; } - @computed get backgroundColor() { - return SettingsManager.userBackgroundColor; - } - @computed get variantColor() { return SettingsManager.userVariantColor; } - @observable isDouble: boolean = false; - render() { if (this.props.children === undefined || this.props.children === null) return null; - else - return ( - <div className="propertiesView-section" onPointerEnter={action(() => this.props.setInSection && this.props.setInSection(true))} onPointerLeave={action(() => this.props.setInSection && this.props.setInSection(false))}> - <div - className="propertiesView-sectionTitle" - onDoubleClick={action(e => { - this.isDouble = true; - this.props.onDoubleClick && this.props.onDoubleClick(); - this.props.setIsOpen(true); - setTimeout(() => (this.isDouble = false), 300); - })} - onClick={action(e => { - this.props.setIsOpen(!this.props.isOpen); - })} - style={{ - background: this.variantColor, - // this.props.isOpen ? this.variantColor : this.backgroundColor, - color: this.color, - }}> - {this.props.title} - <div className="propertiesView-sectionTitle-icon"> - <FontAwesomeIcon icon={this.props.isOpen ? 'caret-down' : 'caret-right'} size="lg" /> - </div> + return ( + <div className="propertiesView-section" onPointerEnter={action(() => this.props.setInSection && this.props.setInSection(true))} onPointerLeave={action(() => this.props.setInSection && this.props.setInSection(false))}> + <div + className="propertiesView-sectionTitle" + onDoubleClick={action(() => { + this.props.onDoubleClick && this.props.onDoubleClick(); + this.props.setIsOpen(true); + })} + onClick={action(() => { + this.props.setIsOpen(!this.props.isOpen); + })} + style={{ + background: this.variantColor, + // this.props.isOpen ? this.variantColor : this.backgroundColor, + color: this.color, + }}> + {this.props.title} + <div className="propertiesView-sectionTitle-icon"> + <FontAwesomeIcon icon={this.props.isOpen ? 'caret-down' : 'caret-right'} size="lg" /> </div> - {!this.props.isOpen ? null : <div className="propertiesView-content">{this.props.children}</div>} </div> - ); + {!this.props.isOpen ? null : <div className="propertiesView-content">{this.props.children}</div>} + </div> + ); } } diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx index 365980f33..8de359e07 100644 --- a/src/client/views/ScriptBox.tsx +++ b/src/client/views/ScriptBox.tsx @@ -1,17 +1,18 @@ +/* eslint-disable react/require-default-props */ import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; +import { emptyFunction } from '../../Utils'; import { Doc, Opt } from '../../fields/Doc'; +import { DocData } from '../../fields/DocSymbols'; import { ScriptField } from '../../fields/ScriptField'; import { ScriptCast } from '../../fields/Types'; -import { emptyFunction } from '../../ClientUtils'; import { DragManager } from '../util/DragManager'; import { CompileScript } from '../util/Scripting'; import { EditableView } from './EditableView'; -import { DocumentIconContainer } from './nodes/DocumentIcon'; import { OverlayView } from './OverlayView'; import './ScriptBox.scss'; -import { DocData } from '../../fields/DocSymbols'; +import { DocumentIconContainer } from './nodes/DocumentIcon'; export interface ScriptBoxProps { onSave: (text: string, onError: (error: string) => void) => void; @@ -25,6 +26,7 @@ export interface ScriptBoxProps { export class ScriptBox extends React.Component<ScriptBoxProps> { @observable private _scriptText: string; + overlayDisposer?: () => void; constructor(props: ScriptBoxProps) { super(props); @@ -42,7 +44,6 @@ export class ScriptBox extends React.Component<ScriptBoxProps> { console.log('ScriptBox: ' + error); }; - overlayDisposer?: () => void; onFocus = () => { this.overlayDisposer?.(); this.overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 }); @@ -53,21 +54,35 @@ export class ScriptBox extends React.Component<ScriptBoxProps> { }; render() { - let onFocus: Opt<() => void> = undefined, - onBlur: Opt<() => void> = undefined; + let onFocus: Opt<() => void>; + let onBlur: Opt<() => void>; if (this.props.showDocumentIcons) { onFocus = this.onFocus; onBlur = this.onBlur; } - const params = <EditableView contents={''} display={'block'} maxHeight={72} height={35} fontSize={28} GetValue={() => ''} SetValue={(value: string) => (this.props.setParams?.(value.split(' ').filter(s => s !== ' ')) ? true : true)} />; + const params = ( + <EditableView + contents="" + display="block" + maxHeight={72} + height={35} + fontSize={28} + GetValue={() => ''} + SetValue={(value: string) => { + this.props.setParams?.(value.split(' ').filter(s => s !== ' ')); + return true; + }} + /> + ); return ( <div className="scriptBox-outerDiv"> <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}> - <textarea className="scriptBox-textarea" onChange={this.onChange} value={this._scriptText} onFocus={onFocus} onBlur={onBlur}></textarea> + <textarea className="scriptBox-textarea" onChange={this.onChange} value={this._scriptText} onFocus={onFocus} onBlur={onBlur} /> <div style={{ background: 'beige' }}>{params}</div> </div> <div className="scriptBox-toolbar"> <button + type="button" onClick={e => { this.props.onSave(this._scriptText, this.onError); e.stopPropagation(); @@ -75,6 +90,7 @@ export class ScriptBox extends React.Component<ScriptBoxProps> { Save </button> <button + type="button" onClick={e => { this.props.onCancel && this.props.onCancel(); e.stopPropagation(); @@ -85,11 +101,12 @@ export class ScriptBox extends React.Component<ScriptBoxProps> { </div> ); } - //let l = docList(this.source[0].data).length; if (l) { let ind = this.target[0].index !== undefined ? (this.target[0].index+1) % l : 0; this.target[0].index = ind; this.target[0].proto = getProto(docList(this.source[0].data)[ind]);} + // let l = docList(this.source[0].data).length; if (l) { let ind = this.target[0].index !== undefined ? (this.target[0].index+1) % l : 0; this.target[0].index = ind; this.target[0].proto = getProto(docList(this.source[0].data)[ind]);} + // eslint-disable-next-line react/sort-comp public static EditButtonScript(title: string, doc: Doc, fieldKey: string, clientX: number, clientY: number, contextParams?: { [name: string]: string }, defaultScript?: ScriptField) { let overlayDisposer: () => void = emptyFunction; const script = ScriptCast(doc[fieldKey]) || defaultScript; - let originalText: string | undefined = undefined; + let originalText: string | undefined; if (script) { originalText = script.script.originalScript; } @@ -124,7 +141,7 @@ export class ScriptBox extends React.Component<ScriptBoxProps> { div.style.display = 'inline-block'; div.style.transform = `translate(${clientX}px, ${clientY}px)`; div.innerHTML = 'button'; - params.length && DragManager.StartButtonDrag([div], text, doc.title + '-instance', {}, params, (button: Doc) => {}, clientX, clientY); + params.length && DragManager.StartButtonDrag([div], text, doc.title + '-instance', {}, params, () => {}, clientX, clientY); doc[DocData][fieldKey] = new ScriptField(script); overlayDisposer(); diff --git a/src/debug/Repl.tsx b/src/debug/Repl.tsx index a9f7c085f..8ac502348 100644 --- a/src/debug/Repl.tsx +++ b/src/debug/Repl.tsx @@ -30,7 +30,7 @@ class Repl extends React.Component { if (!script.compiled) { this.executedCommands.push({ command: this.text, result: 'Compile Error' }); } else { - const result = script.run({ makeInterface }, e => this.executedCommands.push({ command: this.text, result: e.message || e })); + const result = script.run({ makeInterface }, err => this.executedCommands.push({ command: this.text, result: err.message || err })); result.success && this.executedCommands.push({ command: this.text, result: result.result }); } this.text = ''; @@ -39,15 +39,13 @@ class Repl extends React.Component { @computed get commands() { - return this.executedCommands.map(command => { - return ( - <div style={{ marginTop: '5px' }}> - <p>{command.command}</p> - {/* <pre>{JSON.stringify(command.result, null, 2)}</pre> */} - <pre>{command.result instanceof RefField || command.result instanceof ObjectField ? 'object' : String(command.result)}</pre> - </div> - ); - }); + return this.executedCommands.map(command => ( + <div style={{ marginTop: '5px' }}> + <p>{command.command}</p> + {/* <pre>{JSON.stringify(command.result, null, 2)}</pre> */} + <pre>{command.result instanceof RefField || command.result instanceof ObjectField ? 'object' : String(command.result)}</pre> + </div> + )); } render() { diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx index c906dcc03..856cf6bd4 100644 --- a/src/debug/Test.tsx +++ b/src/debug/Test.tsx @@ -1,11 +1,12 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom/client'; -console.log('ENTERED'); + class Test extends React.Component { render() { return <div> HELLO WORLD </div>; } } + const root = ReactDOM.createRoot(document.getElementById('root')!); root.render(<Test />); diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx index fc62463e6..f3295a6c6 100644 --- a/src/debug/Viewer.tsx +++ b/src/debug/Viewer.tsx @@ -1,7 +1,12 @@ +/* eslint-disable react/no-unescaped-entities */ +/* eslint-disable react/button-has-type */ +/* eslint-disable no-use-before-define */ +/* eslint-disable react/no-array-index-key */ +/* eslint-disable no-redeclare */ import { action, configure, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import * as ReactDOM from 'react-dom'; +import * as ReactDOM from 'react-dom/client'; import { DocServer } from '../client/DocServer'; import { resolvedPorts } from '../client/util/CurrentUserUtils'; import { CompileScript } from '../client/util/Scripting'; @@ -86,14 +91,12 @@ class DocumentViewer extends React.Component<{ field: Doc }> { let content; if (this.expanded) { const keys = Object.keys(this.props.field); - const fields = keys.map(key => { - return ( - <div key={key}> - <b>({key}): </b> - <DebugViewer field={this.props.field[key]} setValue={value => applyToDoc(this.props.field, key, value)}></DebugViewer> - </div> - ); - }); + const fields = keys.map(key => ( + <div key={key}> + <b>({key}): </b> + <DebugViewer field={this.props.field[key]} setValue={value => applyToDoc(this.props.field, key, value)} /> + </div> + )); content = ( <div> Document ({this.props.field[Id]})<div style={{ paddingLeft: '25px' }}>{fields}</div> @@ -115,7 +118,7 @@ class DocumentViewer extends React.Component<{ field: Doc }> { class DebugViewer extends React.Component<{ field: FieldResult; setValue(value: string): boolean }> { render() { let content; - const field = this.props.field; + const { field } = this.props; if (field instanceof List) { content = <ListViewer field={field} />; } else if (field instanceof Doc) { @@ -171,7 +174,7 @@ class Viewer extends React.Component { <input value={this.idToAdd} onChange={this.inputOnChange} onKeyDown={this.onKeyDown} /> <div> {this.fields.map((field, index) => ( - <DebugViewer field={field} key={index} setValue={() => false}></DebugViewer> + <DebugViewer field={field} key={index} setValue={() => false} /> ))} </div> </> @@ -181,10 +184,13 @@ class Viewer extends React.Component { (async function () { await DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, 'viewer'); - ReactDOM.render( - <div style={{ position: 'absolute', width: '100%', height: '100%' }}> - <Viewer /> - </div>, - document.getElementById('root') - ); + const root = document.getElementById('root'); + if (root) { + const reactDom = ReactDOM.createRoot(root); + reactDom.render( + <div style={{ position: 'absolute', width: '100%', height: '100%' }}> + <Viewer /> + </div> + ); + } })(); diff --git a/src/extensions/Extensions.ts b/src/extensions/Extensions.ts new file mode 100644 index 000000000..5815527d1 --- /dev/null +++ b/src/extensions/Extensions.ts @@ -0,0 +1,9 @@ +import { Assign as ArrayAssign } from './Extensions_Array'; +import { Assign as StringAssign } from './Extensions_String'; + +function AssignAllExtensions() { + ArrayAssign(); + StringAssign(); +} + +export { AssignAllExtensions }; diff --git a/src/extensions/General/ExtensionsTypings.ts b/src/extensions/ExtensionsTypings.ts index 370157ed0..d6ffd3be3 100644 --- a/src/extensions/General/ExtensionsTypings.ts +++ b/src/extensions/ExtensionsTypings.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ interface Array<T> { lastElement(): T; } @@ -5,4 +6,4 @@ interface Array<T> { interface String { removeTrailingNewlines(): string; hasNewline(): boolean; -}
\ No newline at end of file +} diff --git a/src/extensions/ArrayExtensions.ts b/src/extensions/Extensions_Array.ts index 8e125766d..a50fb330f 100644 --- a/src/extensions/ArrayExtensions.ts +++ b/src/extensions/Extensions_Array.ts @@ -8,12 +8,12 @@ export default class ArrayExtension { } assign() { + // eslint-disable-next-line no-extend-native Object.defineProperty(Array.prototype, this.property, { value: this.body, - enumerable: false + enumerable: false, }); } - } /** @@ -22,16 +22,16 @@ export default class ArrayExtension { * Typescript will not recognize your new function. */ const extensions = [ - new ArrayExtension("lastElement", function () { + new ArrayExtension('lastElement', function () { if (!this.length) { return undefined; } return this[this.length - 1]; - }) + }), ]; function Assign() { extensions.forEach(extension => extension.assign()); } -export { Assign };
\ No newline at end of file +export { Assign }; diff --git a/src/extensions/StringExtensions.ts b/src/extensions/Extensions_String.ts index 2c76e56c8..c95095f8e 100644 --- a/src/extensions/StringExtensions.ts +++ b/src/extensions/Extensions_String.ts @@ -1,17 +1,16 @@ +/* eslint-disable no-extend-native */ function Assign() { - String.prototype.removeTrailingNewlines = function () { let sliced = this; - while (sliced.endsWith("\n")) { + while (sliced.endsWith('\n')) { sliced = sliced.substring(0, this.length - 1); } return sliced as string; }; String.prototype.hasNewline = function () { - return this.endsWith("\n"); + return this.endsWith('\n'); }; - } -export { Assign };
\ No newline at end of file +export { Assign }; diff --git a/src/extensions/General/Extensions.ts b/src/extensions/General/Extensions.ts deleted file mode 100644 index 4b6d05d5f..000000000 --- a/src/extensions/General/Extensions.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Assign as ArrayAssign } from "../ArrayExtensions"; -import { Assign as StringAssign } from "../StringExtensions"; - -function AssignAllExtensions() { - ArrayAssign(); - StringAssign(); -} - -export { AssignAllExtensions };
\ No newline at end of file diff --git a/src/fields/DateField.ts b/src/fields/DateField.ts index 56a3177f8..f0a851ce6 100644 --- a/src/fields/DateField.ts +++ b/src/fields/DateField.ts @@ -1,4 +1,4 @@ -import { serializable, date } from 'serializr'; +import { serializable, date as serializrDate } from 'serializr'; import { Deserializable } from '../client/util/SerializationHelper'; import { ObjectField } from './ObjectField'; import { Copy, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols'; @@ -7,7 +7,7 @@ import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGloba @scriptingGlobal @Deserializable('date') export class DateField extends ObjectField { - @serializable(date()) + @serializable(serializrDate()) readonly date: Date; constructor(date: Date = new Date()) { diff --git a/src/fields/HtmlField.ts b/src/fields/HtmlField.ts index b67f0f7e9..536f5ce4c 100644 --- a/src/fields/HtmlField.ts +++ b/src/fields/HtmlField.ts @@ -1,7 +1,7 @@ +import { primitive, serializable } from 'serializr'; import { Deserializable } from '../client/util/SerializationHelper'; -import { serializable, primitive } from 'serializr'; -import { ObjectField } from './ObjectField'; import { Copy, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols'; +import { ObjectField } from './ObjectField'; @Deserializable('html') export class HtmlField extends ObjectField { diff --git a/src/fields/IconField.ts b/src/fields/IconField.ts index 4d2badb68..33e5be7af 100644 --- a/src/fields/IconField.ts +++ b/src/fields/IconField.ts @@ -1,5 +1,5 @@ -import { Deserializable } from '../client/util/SerializationHelper'; import { serializable, primitive } from 'serializr'; +import { Deserializable } from '../client/util/SerializationHelper'; import { ObjectField } from './ObjectField'; import { Copy, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols'; diff --git a/src/fields/List.ts b/src/fields/List.ts index f97f208fe..852b05bc6 100644 --- a/src/fields/List.ts +++ b/src/fields/List.ts @@ -1,5 +1,5 @@ import { action, computed, makeObservable, observable } from 'mobx'; -import { alias, list, serializable } from 'serializr'; +import { alias, list as serializrList, serializable } from 'serializr'; import { ScriptingGlobals } from '../client/util/ScriptingGlobals'; import { Deserializable, afterDocDeserialize, autoObject } from '../client/util/SerializationHelper'; import { Field, FieldType, StrListCast } from './Doc'; @@ -279,7 +279,7 @@ class ListImpl<T extends FieldType> extends ObjectField { return this[FieldTuples].map(toRealField); } - @serializable(alias(ListFieldName, list(autoObject(), { afterDeserialize: afterDocDeserialize }))) + @serializable(alias(ListFieldName, serializrList(autoObject(), { afterDeserialize: afterDocDeserialize }))) private get __fieldTuples() { return this[FieldTuples]; } diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts index ed603e5de..7217bec37 100644 --- a/src/fields/Schema.ts +++ b/src/fields/Schema.ts @@ -1,3 +1,7 @@ +/* eslint-disable guard-for-in */ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable no-redeclare */ +/* eslint-disable no-use-before-define */ import { Interface, ToInterface, Cast, ToConstructor, HasTail, Head, Tail, ListSpec, ToType, DefaultFieldConstructor } from './Types'; import { Doc, FieldType } from './Doc'; import { ObjectField } from './ObjectField'; @@ -12,6 +16,7 @@ type AllToInterface<T extends Interface[]> = { export const emptySchema = createSchema({}); export const Document = makeInterface(emptySchema); +// eslint-disable-next-line no-redeclare export type Document = makeInterface<[typeof emptySchema]>; export interface InterfaceFunc<T extends Interface[]> { @@ -39,6 +44,7 @@ export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFu // defaultSpec return Cast(field, desc.type, desc.defaultVal); } + // eslint-disable-next-line no-prototype-builtins if (typeof desc === 'function' && !ObjectField.isPrototypeOf(desc) && !RefField.isPrototypeOf(desc)) { const doc = Cast(field, Doc); if (doc === undefined) { @@ -73,8 +79,8 @@ export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc) get() { return Cast(this.__doc[key], type as any); }, - set(value) { - value = Cast(value, type as any); + set(setValue) { + const value = Cast(setValue, type as any); if (value !== undefined) { this.__doc[key] = value; return; @@ -93,14 +99,15 @@ export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc) }; } +// eslint-disable-next-line @typescript-eslint/no-unused-vars export function createSchema<T extends Interface>(schema: T): T & { proto: ToConstructor<Doc> } { return undefined as any; - (schema as any).proto = Doc; - return schema as any; + // (schema as any).proto = Doc; + // return schema as any; } export function listSpec<U extends ToConstructor<FieldType>>(type: U): ListSpec<ToType<U>> { - return { List: type as any }; //TODO Types + return { List: type as any }; // TODO Types } export function defaultSpec<T extends ToConstructor<FieldType>>(type: T, defaultVal: ToType<T>): DefaultFieldConstructor<ToType<T>> { diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts index c6c51957d..3a83e7ca0 100644 --- a/src/fields/URLField.ts +++ b/src/fields/URLField.ts @@ -16,16 +16,18 @@ export abstract class URLField extends ObjectField { @serializable(url()) readonly url: URL; - constructor(url: string); - constructor(url: URL); - constructor(url: URL | string) { + constructor(urlVal: string); + // eslint-disable-next-line @typescript-eslint/no-shadow + constructor(urlVal: URL); + // eslint-disable-next-line @typescript-eslint/no-shadow + constructor(urlVal: URL | string) { super(); this.url = - typeof url !== 'string' - ? url // it's an URL - : url.startsWith('http') - ? new URL(url) - : new URL(url, window.location.origin); + typeof urlVal !== 'string' + ? urlVal // it's an URL + : urlVal.startsWith('http') + ? new URL(urlVal) + : new URL(urlVal, window.location.origin); } [ToScriptString]() { diff --git a/src/mobile/MobileMain.tsx b/src/mobile/MobileMain.tsx index dc3a73def..07839b6f6 100644 --- a/src/mobile/MobileMain.tsx +++ b/src/mobile/MobileMain.tsx @@ -3,7 +3,7 @@ import * as ReactDOM from 'react-dom'; import { DocServer } from '../client/DocServer'; import { Docs } from '../client/documents/Documents'; import { CurrentUserUtils } from '../client/util/CurrentUserUtils'; -import { AssignAllExtensions } from '../extensions/General/Extensions'; +import { AssignAllExtensions } from '../extensions/Extensions'; import { MobileInterface } from './MobileInterface'; AssignAllExtensions(); diff --git a/src/pen-gestures/ndollar.ts b/src/pen-gestures/ndollar.ts index d28d303b8..5f1ea00dc 100644 --- a/src/pen-gestures/ndollar.ts +++ b/src/pen-gestures/ndollar.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-use-before-define */ import { Gestures } from './GestureTypes'; /** @@ -69,20 +70,34 @@ import { Gestures } from './GestureTypes'; * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - **/ + * */ // // Point class // export class Point { - constructor(public X: number, public Y: number) {} + // eslint-disable-next-line no-useless-constructor + constructor( + public X: number, + public Y: number + ) { + /* empty */ + } } // // Rectangle class // export class Rectangle { - constructor(public X: number, public Y: number, public Width: number, public Height: number) {} + // eslint-disable-next-line no-useless-constructor + constructor( + public X: number, + public Y: number, + public Width: number, + public Height: number + ) { + /* empty */ + } } // @@ -93,7 +108,11 @@ export class Unistroke { public StartUnitVector: Point; public Vector: number[]; - constructor(public Name: string, useBoundedRotationInvariance: boolean, points: Point[]) { + constructor( + public Name: string, + useBoundedRotationInvariance: boolean, + points: Point[] + ) { this.Points = Resample(points, NumPoints); const radians = IndicativeAngle(this.Points); this.Points = RotateBy(this.Points, -radians); @@ -121,15 +140,15 @@ export class Multistroke { this.NumStrokes = strokes.length; // number of individual strokes const order = new Array(strokes.length); // array of integer indices - for (var i = 0; i < strokes.length; i++) { + for (let i = 0; i < strokes.length; i++) { order[i] = i; // initialize } - const orders = new Array(); // array of integer arrays - HeapPermute(strokes.length, order, /*out*/ orders); + const orders = [] as any[]; // array of integer arrays + HeapPermute(strokes.length, order, /* out */ orders); const unistrokes = MakeUnistrokes(strokes, orders); // returns array of point arrays this.Unistrokes = new Array(unistrokes.length); // unistrokes for this multistroke - for (var j = 0; j < unistrokes.length; j++) { + for (let j = 0; j < unistrokes.length; j++) { this.Unistrokes[j] = new Unistroke(this.Name, useBoundedRotationInvariance, unistrokes[j]); } } @@ -139,7 +158,14 @@ export class Multistroke { // Result class // export class Result { - constructor(public Name: string, public Score: any, public Time: any) {} + // eslint-disable-next-line no-useless-constructor + constructor( + public Name: string, + public Score: any, + public Time: any + ) { + /* empty */ + } } // @@ -174,44 +200,40 @@ export class NDollarRecognizer { new Multistroke( Gestures.Rectangle, useBoundedRotationInvariance, - new Array( - new Array( - new Point(30, 146), //new Point(29, 160), new Point(30, 180), new Point(31, 200), - new Point(30, 222), //new Point(50, 219), new Point(70, 225), new Point(90, 230), - new Point(106, 225), //new Point(100, 200), new Point(106, 180), new Point(110, 160), - new Point(106, 146), //new Point(80, 150), new Point(50, 146), - new Point(30, 143) - ) - ) + new Array([ + new Point(30, 146), // new Point(29, 160), new Point(30, 180), new Point(31, 200), + new Point(30, 222), // new Point(50, 219), new Point(70, 225), new Point(90, 230), + new Point(106, 225), // new Point(100, 200), new Point(106, 180), new Point(110, 160), + new Point(106, 146), // new Point(80, 150), new Point(50, 146), + new Point(30, 143), + ]) ) ); - this.Multistrokes.push(new Multistroke(Gestures.Line, useBoundedRotationInvariance, new Array(new Array(new Point(12, 347), new Point(119, 347))))); + this.Multistrokes.push(new Multistroke(Gestures.Line, useBoundedRotationInvariance, [[new Point(12, 347), new Point(119, 347)]])); this.Multistrokes.push( new Multistroke( Gestures.Triangle, // equilateral useBoundedRotationInvariance, - new Array(new Array(new Point(40, 100), new Point(100, 200), new Point(140, 102), new Point(42, 100))) + new Array([new Point(40, 100), new Point(100, 200), new Point(140, 102), new Point(42, 100)]) ) ); this.Multistrokes.push( new Multistroke( Gestures.Circle, useBoundedRotationInvariance, - new Array( - new Array( - new Point(200, 250), - new Point(240, 230), - new Point(248, 210), - new Point(248, 190), - new Point(240, 170), - new Point(200, 150), - new Point(160, 170), - new Point(151, 190), - new Point(151, 210), - new Point(160, 230), - new Point(201, 250) - ) - ) + new Array([ + new Point(200, 250), + new Point(240, 230), + new Point(248, 210), + new Point(248, 190), + new Point(240, 170), + new Point(200, 150), + new Point(160, 170), + new Point(151, 190), + new Point(151, 210), + new Point(160, 230), + new Point(201, 250), + ]) ) ); NumMultistrokes = this.Multistrokes.length; // NumMultistrokes flags the end of the non user-defined gstures strokes @@ -292,20 +314,21 @@ export class NDollarRecognizer { const points = CombineStrokes(strokes); // make one connected unistroke from the given strokes const candidate = new Unistroke('', useBoundedRotationInvariance, points); - var u = -1; - var b = +Infinity; + let u = -1; + let b = +Infinity; for ( - var i = 0; + let i = 0; i < this.Multistrokes.length; i++ // for each multistroke template ) { if (!requireSameNoOfStrokes || strokes.length === this.Multistrokes[i].NumStrokes) { // optional -- only attempt match when same # of component strokes - for (const unistroke of this.Multistrokes[i].Unistrokes) { + // eslint-disable-next-line no-loop-func + this.Multistrokes[i].Unistrokes.forEach(unistroke => { // for each unistroke within this multistroke if (AngleBetweenUnitVectors(candidate.StartUnitVector, unistroke.StartUnitVector) <= AngleSimilarityThreshold) { // strokes start in the same direction - var d; + let d; if (useProtractor) { d = OptimalCosineDistance(unistroke.Vector, candidate.Vector); // Protractor } else { @@ -316,7 +339,7 @@ export class NDollarRecognizer { u = i; // multistroke owner of unistroke } } - } + }); } } const t1 = Date.now(); @@ -325,12 +348,12 @@ export class NDollarRecognizer { AddGesture = (name: string, useBoundedRotationInvariance: boolean, strokes: any[]) => { this.Multistrokes[this.Multistrokes.length] = new Multistroke(name, useBoundedRotationInvariance, strokes); - var num = 0; - for (const multistroke of this.Multistrokes) { + let num = 0; + this.Multistrokes.forEach(multistroke => { if (multistroke.Name === name) { num++; } - } + }); return num; }; @@ -343,11 +366,11 @@ export class NDollarRecognizer { // // Private helper functions from here on down // -function HeapPermute(n: number, order: any[], /*out*/ orders: any[]) { +function HeapPermute(n: number, order: any[], /* out */ orders: any[]) { if (n === 1) { orders[orders.length] = order.slice(); // append copy } else { - for (var i = 0; i < n; i++) { + for (let i = 0; i < n; i++) { HeapPermute(n - 1, order, orders); if (n % 2 === 1) { // swap 0, n-1 @@ -364,47 +387,44 @@ function HeapPermute(n: number, order: any[], /*out*/ orders: any[]) { } } -function MakeUnistrokes(strokes: any, orders: any) { - const unistrokes = new Array(); // array of point arrays - for (const order of orders) { +function MakeUnistrokes(strokes: any, orders: any[]) { + const unistrokes = [] as any[]; // array of point arrays + orders.forEach(order => { for ( - var b = 0; - b < Math.pow(2, order.length); + let b = 0; + b < order.length ** 2; b++ // use b's bits for directions ) { - const unistroke = new Array(); // array of points - for (var i = 0; i < order.length; i++) { - var pts; + const unistroke = [] as any[]; // array of points + for (let i = 0; i < order.length; i++) { + let pts; + // eslint-disable-next-line no-bitwise if (((b >> i) & 1) === 1) { // is b's bit at index i on? pts = strokes[order[i]].slice().reverse(); // copy and reverse } else { pts = strokes[order[i]].slice(); // copy } - for (const point of pts) { + pts.forEach((point: any) => { unistroke[unistroke.length] = point; // append points - } + }); } unistrokes[unistrokes.length] = unistroke; // add one unistroke to set } - } + }); return unistrokes; } -function CombineStrokes(strokes: any) { - const points = new Array(); - for (const stroke of strokes) { - for (const { X, Y } of stroke) { - points[points.length] = new Point(X, Y); - } - } +function CombineStrokes(strokes: any[]) { + const points: Point[] = []; + strokes.forEach(stroke => stroke.forEach((X: any, Y: any) => points.push(new Point(X, Y)))); return points; } function Resample(points: any, n: any) { const I = PathLength(points) / (n - 1); // interval length - var D = 0.0; + let D = 0.0; const newpoints = new Array(points[0]); - for (var i = 1; i < points.length; i++) { + for (let i = 1; i < points.length; i++) { const d = Distance(points[i - 1], points[i]); if (D + d >= I) { const qx = points[i - 1].X + ((I - D) / d) * (points[i].X - points[i - 1].X); @@ -425,55 +445,55 @@ function IndicativeAngle(points: any) { const c = Centroid(points); return Math.atan2(c.Y - points[0].Y, c.X - points[0].X); } -function RotateBy(points: any, radians: any) { +function RotateBy(points: Point[], radians: any) { // rotates points around centroid const c = Centroid(points); const cos = Math.cos(radians); const sin = Math.sin(radians); - const newpoints = new Array(); - for (const point of points) { + const newpoints: Point[] = []; + points.forEach(point => { const qx = (point.X - c.X) * cos - (point.Y - c.Y) * sin + c.X; const qy = (point.X - c.X) * sin + (point.Y - c.Y) * cos + c.Y; - newpoints[newpoints.length] = new Point(qx, qy); - } + newpoints.push(new Point(qx, qy)); + }); return newpoints; } function ScaleDimTo(points: any, size: any, ratio1D: any) { // scales bbox uniformly for 1D, non-uniformly for 2D const B = BoundingBox(points); const uniformly = Math.min(B.Width / B.Height, B.Height / B.Width) <= ratio1D; // 1D or 2D gesture test - const newpoints = new Array(); - for (const { X, Y } of points) { + const newpoints: Point[] = []; + points.forEach((X: any, Y: any) => { const qx = uniformly ? X * (size / Math.max(B.Width, B.Height)) : X * (size / B.Width); const qy = uniformly ? Y * (size / Math.max(B.Width, B.Height)) : Y * (size / B.Height); newpoints[newpoints.length] = new Point(qx, qy); - } + }); return newpoints; } function TranslateTo(points: any, pt: any) { // translates points' centroid const c = Centroid(points); - const newpoints = new Array(); - for (const { X, Y } of points) { + const newpoints: Point[] = []; + points.forEach((X: any, Y: any) => { const qx = X + pt.X - c.X; const qy = Y + pt.Y - c.Y; newpoints[newpoints.length] = new Point(qx, qy); - } + }); return newpoints; } function Vectorize(points: any, useBoundedRotationInvariance: any) { // for Protractor - var cos = 1.0; - var sin = 0.0; + let cos = 1.0; + let sin = 0.0; if (useBoundedRotationInvariance) { const iAngle = Math.atan2(points[0].Y, points[0].X); const baseOrientation = (Math.PI / 4.0) * Math.floor((iAngle + Math.PI / 8.0) / (Math.PI / 4.0)); cos = Math.cos(baseOrientation - iAngle); sin = Math.sin(baseOrientation - iAngle); } - var sum = 0.0; - const vector = new Array<number>(); - for (var i = 0; i < points.length; i++) { + let sum = 0.0; + const vector: number[] = []; + for (let i = 0; i < points.length; i++) { const newX = points[i].X * cos - points[i].Y * sin; const newY = points[i].Y * cos + points[i].X * sin; vector[vector.length] = newX; @@ -481,16 +501,16 @@ function Vectorize(points: any, useBoundedRotationInvariance: any) { sum += newX * newX + newY * newY; } const magnitude = Math.sqrt(sum); - for (var i = 0; i < vector.length; i++) { + for (let i = 0; i < vector.length; i++) { vector[i] /= magnitude; } return vector; } function OptimalCosineDistance(v1: any, v2: any) { // for Protractor - var a = 0.0; - var b = 0.0; - for (var i = 0; i < v1.length; i += 2) { + let a = 0.0; + let b = 0.0; + for (let i = 0; i < v1.length; i += 2) { a += v1[i] * v2[i] + v1[i + 1] * v2[i + 1]; b += v1[i] * v2[i + 1] - v1[i + 1] * v2[i]; } @@ -498,18 +518,20 @@ function OptimalCosineDistance(v1: any, v2: any) { return Math.acos(a * Math.cos(angle) + b * Math.sin(angle)); } function DistanceAtBestAngle(points: any, T: any, a: any, b: any, threshold: any) { - var x1 = Phi * a + (1.0 - Phi) * b; - var f1 = DistanceAtAngle(points, T, x1); - var x2 = (1.0 - Phi) * a + Phi * b; - var f2 = DistanceAtAngle(points, T, x2); + let x1 = Phi * a + (1.0 - Phi) * b; + let f1 = DistanceAtAngle(points, T, x1); + let x2 = (1.0 - Phi) * a + Phi * b; + let f2 = DistanceAtAngle(points, T, x2); while (Math.abs(b - a) > threshold) { if (f1 < f2) { + // eslint-disable-next-line no-param-reassign b = x2; x2 = x1; f2 = f1; x1 = Phi * a + (1.0 - Phi) * b; f1 = DistanceAtAngle(points, T, x1); } else { + // eslint-disable-next-line no-param-reassign a = x1; x1 = x2; f1 = f2; @@ -523,34 +545,34 @@ function DistanceAtAngle(points: any, T: any, radians: any) { const newpoints = RotateBy(points, radians); return PathDistance(newpoints, T.Points); } -function Centroid(points: any) { - var x = 0.0, - y = 0.0; - for (const point of points) { +function Centroid(points: Point[]) { + let x = 0.0; + let y = 0.0; + points.forEach(point => { x += point.X; y += point.Y; - } + }); x /= points.length; y /= points.length; return new Point(x, y); } -function BoundingBox(points: any) { - var minX = +Infinity, - maxX = -Infinity, - minY = +Infinity, - maxY = -Infinity; - for (const { X, Y } of points) { +function BoundingBox(points: Point[]) { + let minX = +Infinity; + let maxX = -Infinity; + let minY = +Infinity; + let maxY = -Infinity; + points.forEach((X: any, Y: any) => { minX = Math.min(minX, X); minY = Math.min(minY, Y); maxX = Math.max(maxX, X); maxY = Math.max(maxY, Y); - } + }); return new Rectangle(minX, minY, maxX - minX, maxY - minY); } function PathDistance(pts1: any, pts2: any) { // average distance between corresponding points in two paths - var d = 0.0; - for (var i = 0; i < pts1.length; i++) { + let d = 0.0; + for (let i = 0; i < pts1.length; i++) { // assumes pts1.length == pts2.length d += Distance(pts1[i], pts2[i]); } @@ -558,8 +580,8 @@ function PathDistance(pts1: any, pts2: any) { } function PathLength(points: any) { // length traversed by a point path - var d = 0.0; - for (var i = 1; i < points.length; i++) { + let d = 0.0; + for (let i = 1; i < points.length; i++) { d += Distance(points[i - 1], points[i]); } return d; @@ -570,9 +592,9 @@ function Distance(p1: any, p2: any) { const dy = p2.Y - p1.Y; return Math.sqrt(dx * dx + dy * dy); } -function CalcStartUnitVector(points: any, index: any) { +function CalcStartUnitVector(points: Point[], index: any) { // start angle from points[0] to points[index] normalized as a unit vector - const v = new Point(points[index]?.X - points[0]?.X, points[index]?.Y - points[0]?.Y); + const v = new Point(points[index].X - points[0].X, points[index].Y - points[0].Y); const len = Math.sqrt(v.X * v.X + v.Y * v.Y); return new Point(v.X / len, v.Y / len); } |