diff options
43 files changed, 333 insertions, 460 deletions
diff --git a/src/client/util/BranchingTrailManager.tsx b/src/client/util/BranchingTrailManager.tsx index 119d103c5..65336812d 100644 --- a/src/client/util/BranchingTrailManager.tsx +++ b/src/client/util/BranchingTrailManager.tsx @@ -15,18 +15,18 @@ export class BranchingTrailManager extends React.Component { public static Instance: BranchingTrailManager; // stack of the history - @observable private slideHistoryStack: String[] = []; - @observable private containsSet: Set<String> = new Set<String>(); + @observable private slideHistoryStack: string[] = []; + @observable private containsSet: Set<string> = new Set<string>(); // docId to Doc map - @observable private docIdToDocMap: Map<String, Doc> = new Map<String, Doc>(); + @observable private docIdToDocMap: Map<string, Doc> = new Map<string, Doc>(); // prev pres to copmare with - @observable private prevPresId: String | null = null; - @action setPrevPres = action((newId: String | null) => { + @observable private prevPresId: string | null = null; + @action setPrevPres = action((newId: string | null) => { this.prevPresId = newId; }); - constructor(props: any) { + constructor(props: object) { super(props); makeObservable(this); if (!BranchingTrailManager.Instance) { @@ -48,7 +48,7 @@ export class BranchingTrailManager extends React.Component { // Doc.AddToMyOverlay(hi); }; - @action setSlideHistoryStack = action((newArr: String[]) => { + @action setSlideHistoryStack = action((newArr: string[]) => { this.slideHistoryStack = newArr; }); diff --git a/src/client/util/reportManager/ReportManager.scss b/src/client/util/reportManager/ReportManager.scss index d82d7fdeb..fd343ac8e 100644 --- a/src/client/util/reportManager/ReportManager.scss +++ b/src/client/util/reportManager/ReportManager.scss @@ -96,12 +96,12 @@ transition: all 0.2s ease; background: transparent; - &:hover { - // border-bottom-color: $text-gray; - } - &:focus { - // border-bottom-color: #4476f7; - } + // &:hover { + // // border-bottom-color: $text-gray; + // } + // &:focus { + // // border-bottom-color: #4476f7; + // } } // View issues diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index 232362c5c..4aaf2d03b 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -38,7 +38,12 @@ background: whitesmoke; } -.contextMenu-item { +.contextMenuItem-Selected { + background: lightgoldenrodyellow; + border-style: none; +} + +.contextMenuItem { // width: 11vw; //10vw height: 25px; //2vh display: flex; //comment out to allow search icon to be inline with search text @@ -59,7 +64,7 @@ text-transform: uppercase; padding-right: 30px; - .contextMenu-item-background { + .contextMenuItem-background { width: 100%; height: 100%; position: absolute; @@ -69,13 +74,7 @@ filter: opacity(0); } - &:hover { - .contextMenu-item-background { - filter: opacity(0.2) !important; - } - } - - .contextMenu-item-icon-background { + .contextMenuItem-icon { pointer-events: all; background-color: transparent; width: 35px; @@ -103,6 +102,8 @@ letter-spacing: 1px; text-transform: uppercase; padding-right: 30px; + align-items: center; + align-self: center; } .contextMenu-item:hover { @@ -115,11 +116,6 @@ cursor: pointer; } -.contextMenu-itemSelected { - background: lightgoldenrodyellow; - border-style: none; -} - .contextMenu-group { // width: 11vw; //10vw height: 30px; //2vh @@ -145,23 +141,24 @@ padding-left: 5px; } -.contextMenu-inlineMenu { - // border-top: solid 1px; //TODO:glr clean -} - .contextMenu-description { margin-left: 5px; text-align: left; display: inline; //need this? } -.search-icon { +.contextMenu-search { margin: 10px; + display: flex; + .contextMenu-searchIcon { + margin-right: 5px; + } } -.search { +.contextMenu-searchInput { margin-left: 10px; padding-left: 10px; border: solid black 1px; border-radius: 5px; + width: 100%; } diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 05634f376..5edb5fc0d 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -8,7 +8,7 @@ import * as React from 'react'; import { DivHeight, DivWidth } from '../../ClientUtils'; import { SnappingManager } from '../util/SnappingManager'; import './ContextMenu.scss'; -import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from './ContextMenuItem'; +import { ContextMenuItem, ContextMenuProps } from './ContextMenuItem'; import { ObservableReactComponent } from './ObservableReactComponent'; @observer @@ -148,13 +148,13 @@ export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean } return wasOpen; }; - @computed get filteredItems(): (OriginalMenuProps | string[])[] { + @computed get filteredItems(): (ContextMenuProps | string[])[] { const searchString = this._searchString.toLowerCase().split(' '); const matches = (descriptions: string[]) => searchString.every(s => descriptions.some(desc => desc.toLowerCase().includes(s))); const flattenItems = (items: ContextMenuProps[], groupFunc: (groupName: string) => string[]) => { - let eles: (OriginalMenuProps | string[])[] = []; + let eles: (ContextMenuProps | string[])[] = []; - const leaves: OriginalMenuProps[] = []; + const leaves: ContextMenuProps[] = []; items.forEach(item => { const { description } = item; const path = groupFunc(description); @@ -165,7 +165,7 @@ export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean } eles = eles.concat(children); } } else if (matches(path)) { - leaves.push(item as OriginalMenuProps); + leaves.push(item as ContextMenuProps); } }); @@ -176,8 +176,8 @@ export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean } return flattenItems(this._items.slice(), name => [name]); } - @computed get flatItems(): OriginalMenuProps[] { - return this.filteredItems.filter(item => !Array.isArray(item)) as OriginalMenuProps[]; + @computed get flatItems(): ContextMenuProps[] { + return this.filteredItems.filter(item => !Array.isArray(item)) as ContextMenuProps[]; } @computed get menuItems() { @@ -228,21 +228,11 @@ export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean } color: SnappingManager.userColor, }}> {!this.itemsNeedSearch ? null : ( - <span className="search-icon"> - <span className="icon-background"> + <span className="contextMenu-search"> + <span className="contextMenu-searchIcon"> <FontAwesomeIcon icon="search" size="lg" /> </span> - <input - ref={this._searchRef} - style={{ color: 'black' }} - className="contextMenu-item contextMenu-description search" - type="text" - placeholder="Filter Menu..." - value={this._searchString} - onKeyDown={this.onKeyDown} - onChange={this.onChange} - autoFocus - /> + <input ref={this._searchRef} style={{ color: 'black' }} className="contextMenu-searchInput" type="text" placeholder="Filter Menu..." value={this._searchString} onKeyDown={this.onKeyDown} onChange={this.onChange} autoFocus /> </span> )} {this.menuItems} @@ -264,7 +254,7 @@ export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean } e.preventDefault(); } else if (e.key === 'Enter' || e.key === 'Tab') { const item = this.flatItems[this._selectedIndex]; - if (item) { + if (item.event) { item.event({ x: this.pageX, y: this.pageY }); } else { // if (this._searchString.startsWith(this._defaultPrefix)) { diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index 3b87ea58b..5b4eb704b 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -8,32 +8,27 @@ import { SnappingManager } from '../util/SnappingManager'; import { UndoManager } from '../util/UndoManager'; import { ObservableReactComponent } from './ObservableReactComponent'; -export interface OriginalMenuProps { +export interface ContextMenuProps { + icon: IconProp | JSX.Element; description: string; - event: (stuff?: unknown) => void; - undoable?: boolean; - noexpand?: boolean; - subitems?: ContextMenuProps[]; - icon: IconProp | JSX.Element; // maybe should be optional (icon?) - closeMenu?: () => void; -} - -export interface SubmenuProps { - description: string; - // eslint-disable-next-line no-use-before-define - subitems: ContextMenuProps[]; - noexpand?: boolean; addDivider?: boolean; - icon: IconProp; // maybe should be optional (icon?) closeMenu?: () => void; -} -export type ContextMenuProps = OriginalMenuProps | SubmenuProps; + subitems?: ContextMenuProps[]; + noexpand?: boolean; // whether to render the submenu items as a flyout from this item, or inline in place of this item + + undoable?: boolean; // whether to wrap the event callback in an UndoBatch or not + event?: (stuff?: unknown) => void; +} @observer export class ContextMenuItem extends ObservableReactComponent<ContextMenuProps & { selected?: boolean }> { - @observable private _items: Array<ContextMenuProps> = []; - @observable private overItem = false; + static readonly HOVER_TIMEOUT = 100; + _hoverTimeout?: NodeJS.Timeout; + _overPosY = 0; + _overPosX = 0; + @observable _items: ContextMenuProps[] = []; + @observable _overItem = false; constructor(props: ContextMenuProps & { selected?: boolean }) { super(props); @@ -41,132 +36,63 @@ export class ContextMenuItem extends ObservableReactComponent<ContextMenuProps & } componentDidMount() { - runInAction(() => { - this._items.length = 0; - }); - if ((this._props as SubmenuProps)?.subitems) { - (this._props as SubmenuProps).subitems?.forEach(i => runInAction(() => this._items.push(i))); - } + runInAction(() => this._items.push(...(this._props.subitems ?? []))); } handleEvent = async (e: React.MouseEvent<HTMLDivElement>) => { - if ('event' in this._props) { + if (this._props.event) { this._props.closeMenu?.(); - const batch = this._props.undoable !== false ? UndoManager.StartBatch(`Click Menu item: ${this._props.description}`) : undefined; + const batch = this._props.undoable ? UndoManager.StartBatch(`Click Menu item: ${this._props.description}`) : undefined; await this._props.event({ x: e.clientX, y: e.clientY }); batch?.end(); } }; - currentTimeout?: NodeJS.Timeout | undefined; - static readonly timeout = 300; - _overPosY = 0; - _overPosX = 0; + setOverItem = (over: boolean) => { + this._hoverTimeout = setTimeout( action(() => { this._overItem = over; }), ContextMenuItem.HOVER_TIMEOUT ); // prettier-ignore + }; + onPointerEnter = (e: React.MouseEvent) => { - if (this.currentTimeout) { - clearTimeout(this.currentTimeout); - this.currentTimeout = undefined; - } - if (this.overItem) { - return; - } + this._hoverTimeout && clearTimeout(this._hoverTimeout); this._overPosY = e.clientY; this._overPosX = e.clientX; - this.currentTimeout = setTimeout( - action(() => { - this.overItem = true; - }), - ContextMenuItem.timeout - ); + !this._overItem && this.setOverItem(true); }; onPointerLeave = () => { - if (this.currentTimeout) { - clearTimeout(this.currentTimeout); - this.currentTimeout = undefined; - } - if (!this.overItem) { - return; - } - this.currentTimeout = setTimeout( - action(() => { - this.overItem = false; - }), - ContextMenuItem.timeout - ); + this._hoverTimeout && clearTimeout(this._hoverTimeout); + this._overItem && this.setOverItem(false); }; - isJSXElement(val: unknown): val is JSX.Element { - return React.isValidElement(val); - } + renderItem = (submenu: JSX.Element[]) => { + const alignItems = this._overPosY < window.innerHeight / 3 ? 'flex-start' : this._overPosY > (window.innerHeight * 2) / 3 ? 'flex-end' : 'center'; + const marginTop = this._overPosY < window.innerHeight / 3 ? '20px' : this._overPosY > (window.innerHeight * 2) / 3 ? '-20px' : ''; + const marginLeft = window.innerWidth - this._overPosX - 50 > 0 ? '90%' : '20%'; - render() { - if ('event' in this._props) { - return ( - <div className={'contextMenu-item' + (this._props.selected ? ' contextMenu-itemSelected' : '')} onPointerDown={this.handleEvent}> - {this._props.icon ? <span className="contextMenu-item-icon-background">{this.isJSXElement(this._props.icon) ? this._props.icon : <FontAwesomeIcon icon={this._props.icon} size="sm" />}</span> : null} - <div className="contextMenu-description">{this._props.description.replace(':', '')}</div> - <div - className="contextMenu-item-background" - style={{ - background: SnappingManager.userColor, - }} - /> - </div> - ); - } - if ('subitems' in this._props) { - const where = !this.overItem ? '' : this._overPosY < window.innerHeight / 3 ? 'flex-start' : this._overPosY > (window.innerHeight * 2) / 3 ? 'flex-end' : 'center'; - const marginTop = !this.overItem ? '' : this._overPosY < window.innerHeight / 3 ? '20px' : this._overPosY > (window.innerHeight * 2) / 3 ? '-20px' : ''; + return ( + <div className={`contextMenuItem${this._props.selected ? '-Selected' : ''}`} // + onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} onPointerDown={this.handleEvent} + style={{ alignItems, borderTop: this._props.addDivider ? 'solid 1px' : undefined }} + > + <div className="contextMenuItem-background" style={{ background: SnappingManager.userColor, filter: this._overItem ? 'opacity(0.2)' : '' }} /> + <span className="contextMenuItem-icon" style={{ alignItems: 'center', alignSelf: 'center' }}> + {React.isValidElement(this._props.icon) ? this._props.icon : this._props.icon ? <FontAwesomeIcon icon={this._props.icon as IconProp} size="sm" /> : null} + </span> + <div className="contextMenu-description"> {this._props.description} </div> + {!submenu.length ? null : ( + !this._overItem ? + <FontAwesomeIcon icon="angle-right" size="lg" style={{ position: 'absolute', right: '10px' }} /> : ( + <div className="contextMenu-subMenu-cont" style={{ marginLeft, marginTop, background: SnappingManager.userBackgroundColor }}> + {submenu} + </div> + ) + )} + </div> + ); // prettier-ignore + }; - // here - const submenu = !this.overItem ? null : ( - <div - className="contextMenu-subMenu-cont" - style={{ - marginLeft: window.innerWidth - this._overPosX - 50 > 0 ? '90%' : '20%', - marginTop, - background: SnappingManager.userBackgroundColor, - }}> - {this._items.map(prop => ( - <ContextMenuItem {...prop} key={prop.description} closeMenu={this._props.closeMenu} /> - ))} - </div> - ); - if (!(this._props as SubmenuProps).noexpand) { - return ( - <div className="contextMenu-inlineMenu"> - {this._items.map(prop => ( - <ContextMenuItem {...prop} key={prop.description} closeMenu={this._props.closeMenu} /> - ))} - </div> - ); - } - return ( - <div - className={'contextMenu-item' + (this._props.selected ? ' contextMenu-itemSelected' : '')} - style={{ alignItems: where, borderTop: this._props.addDivider ? 'solid 1px' : undefined }} - onMouseLeave={this.onPointerLeave} - onMouseEnter={this.onPointerEnter}> - {this._props.icon ? ( - <span className="contextMenu-item-icon-background" onMouseEnter={this.onPointerLeave} style={{ alignItems: 'center', alignSelf: 'center' }}> - <FontAwesomeIcon icon={this._props.icon} size="sm" /> - </span> - ) : null} - <div className="contextMenu-description" onMouseEnter={this.onPointerEnter} style={{ alignItems: 'center', alignSelf: 'center' }}> - {this._props.description} - <FontAwesomeIcon icon="angle-right" size="lg" style={{ position: 'absolute', right: '10px' }} /> - </div> - <div - className="contextMenu-item-background" - style={{ - background: SnappingManager.userColor, - }} - /> - {submenu} - </div> - ); - } - return null; + render() { + const submenu = this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this._props.closeMenu} />); + return this.props.event || this._props.noexpand ? this.renderItem(submenu) : <div className="contextMenu-inlineMenu">{submenu}</div>; } } diff --git a/src/client/views/DictationOverlay.tsx b/src/client/views/DictationOverlay.tsx index b242acdba..e33049d3b 100644 --- a/src/client/views/DictationOverlay.tsx +++ b/src/client/views/DictationOverlay.tsx @@ -17,7 +17,7 @@ export class DictationOverlay extends React.Component { // eslint-disable-next-line react/no-unused-class-component-methods public hasActiveModal = false; - constructor(props: any) { + constructor(props: object) { super(props); makeObservable(this); DictationOverlay.Instance = this; diff --git a/src/client/views/DocViewUtils.ts b/src/client/views/DocViewUtils.ts index 1f5f29c7e..49a30aa08 100644 --- a/src/client/views/DocViewUtils.ts +++ b/src/client/views/DocViewUtils.ts @@ -6,6 +6,7 @@ import { Doc, SetActiveAudioLinker } from '../../fields/Doc'; import { DocUtils } from '../documents/DocUtils'; import { FieldViewProps } from './nodes/FieldView'; +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace DocViewUtils { export const ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 487868169..b778a4fb9 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -1,6 +1,3 @@ -/* eslint-disable jsx-a11y/control-has-associated-label */ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ import { IconLookup, IconProp } from '@fortawesome/fontawesome-svg-core'; import { faCalendarDays } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -20,7 +17,7 @@ import { DictationManager } from '../util/DictationManager'; import { DragManager } from '../util/DragManager'; import { dropActionType } from '../util/DropActionTypes'; import { SharingManager } from '../util/SharingManager'; -import { UndoManager, undoBatch } from '../util/UndoManager'; +import { UndoManager, undoable } from '../util/UndoManager'; import './DocumentButtonBar.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; import { PinProps } from './PinFuncs'; @@ -33,12 +30,12 @@ import { OpenWhere } from './nodes/OpenWhere'; import { DashFieldView } from './nodes/formattedText/DashFieldView'; @observer -export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (DocumentView | undefined)[]; stack?: any }> { +export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (DocumentView | undefined)[]; stack?: unknown }> { private _dragRef = React.createRef<HTMLDivElement>(); // eslint-disable-next-line no-use-before-define public static Instance: DocumentButtonBar; - constructor(props: any) { + constructor(props: { views: () => (DocumentView | undefined)[]; stack?: unknown }) { super(props); makeObservable(this); DocumentButtonBar.Instance = this; @@ -83,7 +80,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( <div className="documentButtonBar-icon documentButtonBar-follow" style={{ backgroundColor: followLink ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: followLink ? Colors.BLACK : Colors.WHITE }} - onClick={undoBatch(() => this._props.views().map(view => view?.toggleFollowLink(undefined, false)))}> + onClick={undoable(() => this._props.views().map(view => view?.toggleFollowLink(undefined, false)), 'follow link')}> <div className="documentButtonBar-followTypes"> {followBtn( true, diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 93c3e3338..68970223a 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -34,6 +34,7 @@ import { DocumentView } from './nodes/DocumentView'; import { ImageBox } from './nodes/ImageBox'; import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; +import { IconProp } from '@fortawesome/fontawesome-svg-core'; interface DocumentDecorationsProps { PanelWidth: number; @@ -145,7 +146,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora titleEntered = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { e.stopPropagation(); - (e.target as any).blur(); + (e.target as HTMLElement).blur?.(); } }; @@ -239,7 +240,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora } }); if (!this._iconifyBatch) { - (document.activeElement as any).blur?.(); + (document.activeElement as HTMLElement).blur?.(); this._iconifyBatch = UndoManager.StartBatch(forceDeleteOrIconify ? 'delete selected docs' : 'iconifying'); } else { // eslint-disable-next-line no-param-reassign @@ -254,7 +255,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora setupMoveUpEvents(this, e, () => DragManager.StartWindowDrag?.(e, [DocumentView.SelectedDocs().lastElement()]) ?? false, emptyFunction, this.onMaximizeClick, false, false); e.stopPropagation(); }; - onMaximizeClick = (e: any): void => { + onMaximizeClick = (e: PointerEvent): void => { const selView = DocumentView.Selected()[0]; if (selView) { if (e.ctrlKey) { @@ -349,8 +350,10 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora setupMoveUpEvents( this, e, - (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, + (moveEv: PointerEvent, down: number[], delta: number[]) => { + this.setRotateCenter(seldocview, [this.rotCenter[0] + delta[0], this.rotCenter[1] + delta[1]]); + return false; + }, action(() => { this._isRotating = false; }), // upEvent action(() => { seldocview.Document._rotation_centerX = seldocview.Document._rotation_centerY = 0; }), true @@ -430,7 +433,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction); e.stopPropagation(); const id = (this._resizeHdlId = e.currentTarget.className); - const pad = id.includes('Left') || id.includes('Right') ? Number(getComputedStyle(e.target as any).width.replace('px', '')) / 2 : 0; + const pad = id.includes('Left') || id.includes('Right') ? Number(getComputedStyle(e.target as HTMLElement).width?.replace('px', '')) / 2 : 0; const bounds = e.currentTarget.getBoundingClientRect(); this._offset = { x: id.toLowerCase().includes('left') ? bounds.right - e.clientX - pad : bounds.left - e.clientX + pad, // @@ -478,7 +481,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora const scaleAspect = {x:scale.x === 1 && hasFixedAspect ? scale.y : scale.x, y: scale.x !== 1 && hasFixedAspect ? scale.x : scale.y}; DocumentView.Selected().forEach(docView => this.resizeView(docView, refPt, scaleAspect, { dragHdl, ctrlKey:e.ctrlKey })); // prettier-ignore - await new Promise<any>(res => { setTimeout(() => { res(this._interactionLock = undefined)})}); + await new Promise<void>(res => { setTimeout(() => { res(this._interactionLock = undefined)})}); }); // prettier-ignore return false; @@ -681,10 +684,10 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora const collectionAcl = docView.containerViewPath?.()?.lastElement() ? GetEffectiveAcl(docView.containerViewPath?.().lastElement().dataDoc) : AclEdit; return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.Document) !== AclAdmin; }); - const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => ( + const topBtn = (key: string, icon: IconProp, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: PointerEvent) => 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, clickEv => click!(clickEv)))}> - <FontAwesomeIcon icon={icon as any} /> + <div className={`documentDecorations-${key}Button`} onContextMenu={e => e.preventDefault()} onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, clickEv => click?.(clickEv)))}> + <FontAwesomeIcon icon={icon} /> </div> </Tooltip> ); diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 684b948af..e02e39b8b 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -1,10 +1,7 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ import { action, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import * as Autosuggest from 'react-autosuggest'; -import { ObjectField } from '../../fields/ObjectField'; import './EditableView.scss'; import { DocumentIconContainer } from './nodes/DocumentIcon'; import { FieldView, FieldViewProps } from './nodes/FieldView'; @@ -29,7 +26,7 @@ export interface EditableProps { /** * The contents to render when not editing */ - contents: any; + contents: string; fieldContents?: FieldViewProps; fontStyle?: string; fontSize?: number; @@ -41,8 +38,8 @@ export interface EditableProps { autosuggestProps?: { resetValue: () => void; value: string; - onChange: (e: React.ChangeEvent, { newValue }: { newValue: string }) => void; - autosuggestProps: Autosuggest.AutosuggestProps<string, any>; + onChange: (e: React.FormEvent, { newValue }: { newValue: string }) => void; + autosuggestProps: Autosuggest.AutosuggestProps<string, unknown>; }; oneLine?: boolean; // whether to display the editable view as a single input line or as a textarea allowCRs?: boolean; // can carriage returns be entered @@ -112,8 +109,8 @@ export class EditableView extends ObservableReactComponent<EditableProps> { } onChange = (e: React.ChangeEvent) => { - const targVal = (e.target as any).value; - if (!(targVal.startsWith(':=') || targVal.startsWith('='))) { + const targVal = (e.target as HTMLSelectElement).value; + if (!(targVal?.startsWith(':=') || targVal?.startsWith('='))) { this._overlayDisposer?.(); this._overlayDisposer = undefined; } else if (!this._overlayDisposer) { @@ -230,13 +227,11 @@ export class EditableView extends ObservableReactComponent<EditableProps> { className: 'editableView-input', onKeyDown: this.onKeyDown, autoFocus: true, - // @ts-ignore - onBlur: e => this.finalizeEdit(e.currentTarget.value, false, true, false), + onBlur: e => this.finalizeEdit((e.currentTarget as HTMLSelectElement).value, false, true, false), onPointerDown: this.stopPropagation, onClick: this.stopPropagation, onPointerUp: this.stopPropagation, value: this._props.autosuggestProps.value, - // @ts-ignore onChange: this._props.autosuggestProps.onChange, }} /> @@ -248,7 +243,6 @@ export class EditableView extends ObservableReactComponent<EditableProps> { placeholder={this._props.placeholder} onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)} defaultValue={this._props.GetValue()} - // eslint-disable-next-line jsx-a11y/no-autofocus autoFocus onChange={this.onChange} onKeyDown={this.onKeyDown} @@ -264,7 +258,6 @@ export class EditableView extends ObservableReactComponent<EditableProps> { placeholder={this._props.placeholder} onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)} defaultValue={this._props.GetValue()} - // eslint-disable-next-line jsx-a11y/no-autofocus autoFocus onChange={this.onChange} onKeyDown={this.onKeyDown} @@ -288,7 +281,7 @@ export class EditableView extends ObservableReactComponent<EditableProps> { ); } setTimeout(() => this._props.autosuggestProps?.resetValue()); - return this._props.contents instanceof ObjectField ? null : ( + return ( <div className={`editableView-container-editing${this._props.oneLine ? '-oneLine' : ''}`} ref={this._ref} @@ -308,10 +301,7 @@ export class EditableView extends ObservableReactComponent<EditableProps> { fontStyle: this._props.fontStyle, fontSize: this._props.fontSize, }}> - { - // eslint-disable-next-line react/jsx-props-no-spreading - this._props.fieldContents ? <FieldView {...this._props.fieldContents} /> : this.props.contents ? this._props.contents?.valueOf() : '' - } + {this._props.fieldContents ? <FieldView {...this._props.fieldContents} /> : this._props.contents ? this._props.contents?.valueOf() : ''} </span> </div> ); diff --git a/src/client/views/ExtractColors.ts b/src/client/views/ExtractColors.ts index f6928c52a..eee1d3a04 100644 --- a/src/client/views/ExtractColors.ts +++ b/src/client/views/ExtractColors.ts @@ -126,7 +126,7 @@ export class ExtractColors { let hue = 0; let saturation = 0; - let lightness = intensity; + const lightness = intensity; if (area !== 0) { saturation = area / (1 - Math.abs(2 * intensity - 1)); diff --git a/src/client/views/FieldsDropdown.tsx b/src/client/views/FieldsDropdown.tsx index 0ea0ebd83..407031b40 100644 --- a/src/client/views/FieldsDropdown.tsx +++ b/src/client/views/FieldsDropdown.tsx @@ -29,7 +29,7 @@ interface fieldsDropdownProps { @observer export class FieldsDropdown extends ObservableReactComponent<fieldsDropdownProps> { @observable _newField = ''; - constructor(props: any) { + constructor(props: fieldsDropdownProps) { super(props); makeObservable(this); } @@ -101,13 +101,13 @@ export class FieldsDropdown extends ObservableReactComponent<fieldsDropdownProps }), }} placeholder={typeof this._props.placeholder === 'string' ? this._props.placeholder : this._props.placeholder?.()} - options={options as any} + options={options} isMulti={false} - onChange={val => this._props.selectFunc((val as any as { value: string; label: string }).value)} + onChange={val => this._props.selectFunc((val as { value: string; label: string }).value)} onKeyDown={e => { if (e.key === 'Enter') { runInAction(() => { - this._props.selectFunc((this._newField = (e.nativeEvent.target as any)?.value)); + this._props.selectFunc((this._newField = (e.nativeEvent.target as HTMLSelectElement)?.value)); }); } e.stopPropagation(); diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 7d01bbabb..a85a03aab 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -56,7 +56,7 @@ export class KeyManager { window.addEventListener('keydown', KeyManager.Instance.handle); window.removeEventListener('keyup', KeyManager.Instance.unhandle); window.addEventListener('keyup', KeyManager.Instance.unhandle); - window.addEventListener('paste', KeyManager.Instance.paste as any); + window.addEventListener('paste', KeyManager.Instance.paste); } public unhandle = action((/* e: KeyboardEvent */) => { @@ -330,7 +330,7 @@ export class KeyManager { } break; case 'c': - if ((document.activeElement as any)?.type !== 'text' && !AnchorMenu.Instance.Active && DocumentDecorations.Instance.Bounds.r - DocumentDecorations.Instance.Bounds.x > 2) { + if (!AnchorMenu.Instance.Active && DocumentDecorations.Instance.Bounds.r - DocumentDecorations.Instance.Bounds.x > 2) { const bds = DocumentDecorations.Instance.Bounds; const pt = DocumentView.Selected()[0] .screenToViewTransform() diff --git a/src/client/views/KeyphraseQueryView.scss b/src/client/views/KeyphraseQueryView.scss deleted file mode 100644 index ac715e5e7..000000000 --- a/src/client/views/KeyphraseQueryView.scss +++ /dev/null @@ -1,8 +0,0 @@ -.fading { - animation: fanOut 1s -} - -@keyframes fanOut { - from {opacity: 0;} - to {opacity: 1;} -}
\ No newline at end of file diff --git a/src/client/views/KeyphraseQueryView.tsx b/src/client/views/KeyphraseQueryView.tsx deleted file mode 100644 index 81f004010..000000000 --- a/src/client/views/KeyphraseQueryView.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/* eslint-disable jsx-a11y/label-has-associated-control */ -import { observer } from 'mobx-react'; -import * as React from 'react'; -import './KeyphraseQueryView.scss'; - -// tslint:disable-next-line: class-name -export interface KP_Props { - keyphrases: string; -} - -@observer -export class KeyphraseQueryView extends React.Component<KP_Props> { - render() { - const keyterms = this.props.keyphrases.split(','); - return ( - <div> - <h5>Select queries to send:</h5> - <form> - {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/MainViewModal.tsx b/src/client/views/MainViewModal.tsx index a6dc5c62b..4a35805fb 100644 --- a/src/client/views/MainViewModal.tsx +++ b/src/client/views/MainViewModal.tsx @@ -1,5 +1,3 @@ -/* 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'; diff --git a/src/client/views/ObservableReactComponent.tsx b/src/client/views/ObservableReactComponent.tsx index 34da82b6c..bb7a07f0e 100644 --- a/src/client/views/ObservableReactComponent.tsx +++ b/src/client/views/ObservableReactComponent.tsx @@ -8,27 +8,27 @@ import JsxParser from 'react-jsx-parser'; * This is an abstract class that serves as the base for a PDF-style or Marquee-style * menu. To use this class, look at PDFMenu.tsx or MarqueeOptionsMenu.tsx for an example. */ -export abstract class ObservableReactComponent<T> extends React.Component<T, {}> { +export abstract class ObservableReactComponent<T> extends React.Component<T, object> { @observable _props: React.PropsWithChildren<T>; - constructor(props: any) { + constructor(props: React.PropsWithChildren<T>) { super(props); this._props = props; makeObservable(this); } componentDidUpdate(prevProps: Readonly<T>): void { Object.keys(prevProps) - .filter(pkey => (prevProps as any)[pkey] !== (this.props as any)[pkey]) + .filter(pkey => (prevProps as {[key:string]: unknown})[pkey] !== (this.props as {[key:string]: unknown})[pkey]) .forEach(action(pkey => { - (this._props as any)[pkey] = (this.props as any)[pkey]; + (this._props as {[key:string]: unknown})[pkey] = (this.props as {[key:string]: unknown})[pkey]; })); // prettier-ignore } } class ObserverJsxParser1 extends JsxParser { - constructor(props: any) { + constructor(props: object) { super(props); - observer(this as any); + observer(this as typeof JsxParser); } } -export const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any; +export const ObserverJsxParser = ObserverJsxParser1 as typeof JsxParser; diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 034ade50b..7e597879d 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -7,13 +7,14 @@ import { Docs, DocumentOptions } from '../documents/Documents'; import { DocUtils } from '../documents/DocUtils'; import { ImageUtils } from '../util/Import & Export/ImageUtils'; import { Transform } from '../util/Transform'; -import { UndoManager, undoBatch } from '../util/UndoManager'; +import { UndoManager, undoable } from '../util/UndoManager'; import { ObservableReactComponent } from './ObservableReactComponent'; import './PreviewCursor.scss'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; +import { StrCast } from '../../fields/Types'; @observer -export class PreviewCursor extends ObservableReactComponent<{}> { +export class PreviewCursor extends ObservableReactComponent<object> { // eslint-disable-next-line no-use-before-define static _instance: PreviewCursor; public static get Instance() { @@ -29,7 +30,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> { @observable _clickPoint: number[] = []; @observable public Visible = false; public Doc: Opt<Doc>; - constructor(props: any) { + constructor(props: object) { super(props); makeObservable(this); PreviewCursor._instance = this; @@ -46,7 +47,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> { }); // tests for URL and makes web document - const re: any = /^https?:\/\//g; + const re = /^https?:\/\//g; const plain = e.clipboardData.getData('text/plain'); if (plain && newPoint) { // tests for youtube and makes video document @@ -64,17 +65,19 @@ export class PreviewCursor extends ObservableReactComponent<{}> { } else if (re.test(plain)) { const url = plain; if (!url.startsWith(window.location.href)) { - undoBatch(() => - this._addDocument?.( - Docs.Create.WebDocument(url, { - title: url, - _width: 500, - _height: 300, - data_useCors: true, - x: newPoint[0], - y: newPoint[1], - }) - ) + undoable( + () => + this._addDocument?.( + Docs.Create.WebDocument(url, { + title: url, + _width: 500, + _height: 300, + data_useCors: true, + x: newPoint[0], + y: newPoint[1], + }) + ), + 'paste web doc' )(); } else alert('cannot paste dash into itself'); } else if (plain.startsWith('__DashDocId(') || plain.startsWith('__DashCloneId(')) { @@ -94,11 +97,11 @@ export class PreviewCursor extends ObservableReactComponent<{}> { } // pasting in images else if (e.clipboardData.getData('text/html') !== '' && e.clipboardData.getData('text/html').includes('<img src=')) { - const regEx: any = /<img src="(.*?)"/g; - const arr: any[] = regEx.exec(e.clipboardData.getData('text/html')); + const regEx = /<img src="(.*?)"/g; + const arr = regEx.exec(e.clipboardData.getData('text/html')); - if (newPoint) { - undoBatch(() => { + if (newPoint && arr) { + undoable(() => { const doc = Docs.Create.ImageDocument(arr[1], { _width: 300, title: arr[1], @@ -107,7 +110,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> { }); ImageUtils.ExtractImgInfo(doc); this._addDocument?.(doc); - })(); + }, 'paste image doc')(); } } else if (e.clipboardData.items.length && newPoint) { const batch = UndoManager.StartBatch('collection view drop'); @@ -196,8 +199,12 @@ 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)` }}> + <div + className="previewCursor" + onBlur={this.onBlur} + tabIndex={0} + ref={e => e?.focus()} + style={{ color: lightOrDark(StrCast(this.Doc?.backgroundColor, 'white')), transform: `translate(${this._clickPoint[0]}px, ${this._clickPoint[1]}px)` }}> I </div> ); diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index edf6df2b9..f346d4ba8 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -1,5 +1,3 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ /* eslint-disable react/no-unused-class-component-methods */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Dropdown, DropdownType, IListItemProps, Toggle, ToggleType, Type } from 'browndash-components'; @@ -18,7 +16,7 @@ import { TfiBarChart } from 'react-icons/tfi'; import { Doc, Opt } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { ScriptField } from '../../fields/ScriptField'; -import { BoolCast, ScriptCast } from '../../fields/Types'; +import { BoolCast, ScriptCast, StrCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; import { DocUtils, IsFollowLinkScript } from '../documents/DocUtils'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; @@ -32,7 +30,7 @@ import { OpenWhere } from './nodes/OpenWhere'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; @observer -export class PropertiesButtons extends React.Component<{}, {}> { +export class PropertiesButtons extends React.Component { // eslint-disable-next-line no-use-before-define @observable public static Instance: PropertiesButtons; @@ -314,13 +312,6 @@ export class PropertiesButtons extends React.Component<{}, {}> { // ); // } - @undoBatch - handlePerspectiveChange = (e: any) => { - this.selectedDoc && (this.selectedDoc._type_collection = e.target.value); - DocumentView.Selected().forEach(docView => { - docView.layoutDoc._type_collection = e.target.value; - }); - }; @computed get onClickVal() { const linkButton = IsFollowLinkScript(this.selectedDoc.onClick); const followLoc = this.selectedDoc._followLinkLocation; @@ -452,7 +443,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { else this.selectedDoc && DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, 'onClick'); }; - propertyToggleBtn = (label: (on?: any) => string, property: string, tooltip: (on?: any) => string, icon: (on?: any) => any, onClick?: (dv: Opt<DocumentView>, doc: Doc, property: string) => void, useUserDoc?: boolean) => { + propertyToggleBtn = (label: (on?: unknown) => string, property: string, tooltip: (on?: unknown) => string, icon: (on?: unknown) => unknown, onClick?: (dv: Opt<DocumentView>, doc: Doc, property: string) => void, useUserDoc?: boolean) => { const targetDoc = useUserDoc ? Doc.UserDoc() : this.selectedLayoutDoc; const onPropToggle = (dv: Opt<DocumentView>, doc: Doc, prop: string) => { (dv?.layoutDoc || doc)[prop] = !(dv?.layoutDoc || doc)[prop]; @@ -463,7 +454,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { tooltip={tooltip(BoolCast(targetDoc[property]))} text={label(targetDoc?.[property])} color={SettingsManager.userColor} - icon={icon(targetDoc?.[property] as any)} + icon={icon(targetDoc?.[property]) as string} iconPlacement="left" align="flex-start" fillWidth @@ -484,7 +475,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { const isImage = layoutField instanceof ImageField; const isMap = this.selectedDoc?.type === DocumentType.MAP; const isCollection = this.selectedDoc?.type === DocumentType.COL; - const isStacking = [CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.NoteTaking].includes(this.selectedDoc?._type_collection as any); + const isStacking = [CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.NoteTaking].includes(StrCast(this.selectedDoc?._type_collection) as CollectionViewType); const isFreeForm = this.selectedDoc?._type_collection === CollectionViewType.Freeform; const isTree = this.selectedDoc?._type_collection === CollectionViewType.Tree; const toggle = (ele: JSX.Element | null, style?: React.CSSProperties) => ( diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx index edb55f341..e30d14eae 100644 --- a/src/client/views/PropertiesDocBacklinksSelector.tsx +++ b/src/client/views/PropertiesDocBacklinksSelector.tsx @@ -16,7 +16,7 @@ import { DocumentView } from './nodes/DocumentView'; type PropertiesDocBacklinksSelectorProps = { Document: Doc; - Stack?: any; + Stack?: string; hideTitle?: boolean; addDocTab(doc: Doc, location: OpenWhere): void; }; diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx index 1fea36d16..f494ff16a 100644 --- a/src/client/views/PropertiesDocContextSelector.tsx +++ b/src/client/views/PropertiesDocContextSelector.tsx @@ -1,6 +1,3 @@ -/* 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'; @@ -15,14 +12,14 @@ import { OpenWhere } from './nodes/OpenWhere'; type PropertiesDocContextSelectorProps = { DocView?: DocumentView; - Stack?: any; + Stack?: string; hideTitle?: boolean; addDocTab(doc: Doc, location: OpenWhere): void; }; @observer export class PropertiesDocContextSelector extends ObservableReactComponent<PropertiesDocContextSelectorProps> { - constructor(props: any) { + constructor(props: PropertiesDocContextSelectorProps) { super(props); makeObservable(this); } diff --git a/src/client/views/PropertiesSection.tsx b/src/client/views/PropertiesSection.tsx index b9a587719..12a46c7a4 100644 --- a/src/client/views/PropertiesSection.tsx +++ b/src/client/views/PropertiesSection.tsx @@ -1,5 +1,3 @@ -/* 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 } from 'mobx'; @@ -12,7 +10,7 @@ export interface PropertiesSectionProps { title: string; children?: JSX.Element | string | null; isOpen: boolean; - setIsOpen: (bool: boolean) => any; + setIsOpen: (bool: boolean) => void; onDoubleClick?: () => void; } diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 024db82a4..5952d6641 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1,7 +1,4 @@ -/* eslint-disable jsx-a11y/click-events-have-key-events */ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable prettier/prettier */ -import { IconLookup } from '@fortawesome/fontawesome-svg-core'; +import { IconLookup, IconProp } from '@fortawesome/fontawesome-svg-core'; import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Checkbox, Tooltip } from '@mui/material'; @@ -12,6 +9,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { ColorResult, SketchPicker } from 'react-color'; import * as Icons from 'react-icons/bs'; // {BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs" +import ResizeObserver from 'resize-observer-polyfill'; import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; import { Doc, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc'; @@ -44,8 +42,6 @@ import { StyleProviderFuncType } from './nodes/FieldView'; import { OpenWhere } from './nodes/OpenWhere'; import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails'; -const _global = (window /* browser */ || global) /* node */ as any; - interface PropertiesViewProps { width: number; height: number; @@ -59,7 +55,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps // eslint-disable-next-line no-use-before-define public static Instance: PropertiesView | undefined; - constructor(props: any) { + constructor(props: PropertiesViewProps) { super(props); makeObservable(this); PropertiesView.Instance = this; @@ -142,7 +138,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps return this.selectedDoc?.isGroup; } @computed get isStack() { - return [CollectionViewType.Masonry, CollectionViewType.Multicolumn, CollectionViewType.Multirow, CollectionViewType.Stacking, CollectionViewType.NoteTaking].includes(this.selectedDoc?.type_collection as any); + return [CollectionViewType.Masonry, CollectionViewType.Multicolumn, CollectionViewType.Multirow, CollectionViewType.Stacking, CollectionViewType.NoteTaking].includes(this.selectedDoc?.type_collection as CollectionViewType); } rtfWidth = () => (!this.selectedLayoutDoc ? 0 : Math.min(NumCast(this.selectedLayoutDoc?._width), this._props.width - 20)); @@ -275,7 +271,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps @observable transform: Transform = Transform.Identity(); getTransform = () => this.transform; propertiesDocViewRef = (ref: HTMLDivElement) => { - const resizeObserver = new _global.ResizeObserver( + const resizeObserver = new ResizeObserver( action(() => { const cliRect = ref.getBoundingClientRect(); this.transform = new Transform(-cliRect.x, -cliRect.y, 1); @@ -357,7 +353,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps * Handles the changing of a user's permissions from the permissions panel. */ @undoBatch - changePermissions = (e: any, user: string) => { + changePermissions = (e: React.ChangeEvent<HTMLSelectElement>, user: string) => { const docs = DocumentView.Selected().length < 2 ? [this.selectedDoc] : DocumentView.Selected().map(dv => (this.layoutDocAcls ? dv.layoutDoc : dv.dataDoc)); SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs, this.layoutDocAcls); }; @@ -456,7 +452,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps /** * Sorting algorithm to sort users. */ - sortUsers = (u1: String, u2: String) => (u1 > u2 ? -1 : u1 === u2 ? 0 : 1); + sortUsers = (u1: string, u2: string) => (u1 > u2 ? -1 : u1 === u2 ? 0 : 1); /** * Sorting algorithm to sort groups. @@ -711,7 +707,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps ); } - inputBox = (key: string, value: any, setter: (val: string) => {}, title: string) => ( + inputBox = (key: string, value: string | number | undefined, setter: (val: string) => void, title: string) => ( <div className="inputBox" style={{ @@ -721,17 +717,29 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps <div className="inputBox-title"> {title} </div> <input className="inputBox-input" type="text" value={value} style={{ color: SnappingManager.userColor, backgroundColor: SnappingManager.userBackgroundColor }} onChange={e => setter(e.target.value)} onKeyDown={e => e.stopPropagation()} /> <div className="inputBox-button"> - <div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}> + <div + className="inputBox-button-up" + key="up2" + onPointerDown={undoable( + action(() => this.upDownButtons('up', key)), + 'down btn' + )}> <FontAwesomeIcon icon="caret-up" size="sm" /> </div> - <div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}> + <div + className="inputbox-Button-down" + key="down2" + onPointerDown={undoable( + action(() => this.upDownButtons('down', key)), + 'up btn' + )}> <FontAwesomeIcon icon="caret-down" size="sm" /> </div> </div> </div> ); - inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => ( + inputBoxDuo = (key: string, value: string | number | undefined, setter: (val: string) => void, title1: string, key2: string, value2: string | number | undefined, setter2: (val: string) => void, title2: string) => ( <div className="inputBox-duo"> {this.inputBox(key, value, setter, title1)} {title2 === '' ? null : this.inputBox(key2, value2, setter2, title2)} @@ -841,7 +849,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps @observable private _fillBtn = false; @observable private _lineBtn = false; - private _lastDash: any = '2'; + private _lastDash: string = '2'; @computed get colorFil() { return StrCast(this.selectedDoc?.[DocData].fillColor); } // prettier-ignore set colorFil(value) { this.selectedDoc && (this.selectedDoc[DocData].fillColor = value || undefined); } // prettier-ignore @@ -917,7 +925,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps ); } - @computed get dashdStk() { return this.selectedDoc?.stroke_dash || ''; } // prettier-ignore + @computed get dashdStk() { return StrCast(this.selectedDoc?.stroke_dash); } // prettier-ignore set dashdStk(value) { value && (this._lastDash = value); this.selectedDoc && (this.selectedDoc[DocData].stroke_dash = value ? this._lastDash : undefined); @@ -939,14 +947,26 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps this.selectedDoc && (this.selectedDoc[DocData].stroke_endMarker = value); } - regInput = (key: string, value: any, setter: (val: string) => {}) => ( + regInput = (key: string, value: string | number | undefined, setter: (val: string) => void) => ( <div className="inputBox"> <input className="inputBox-input" type="text" value={value} style={{ color: SnappingManager.userColor, backgroundColor: SnappingManager.userBackgroundColor }} onChange={e => setter(e.target.value)} /> <div className="inputBox-button"> - <div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}> + <div + className="inputBox-button-up" + key="up2" + onPointerDown={undoable( + action(() => this.upDownButtons('up', key)), + 'up' + )}> <FontAwesomeIcon icon="caret-up" size="sm" /> </div> - <div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}> + <div + className="inputbox-Button-down" + key="down2" + onPointerDown={undoable( + action(() => this.upDownButtons('down', key)), + 'down' + )}> <FontAwesomeIcon icon="caret-down" size="sm" /> </div> </div> @@ -1002,7 +1022,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps className="arrows-head-input" type="checkbox" checked={this.markHead !== ''} - onChange={undoBatch(action(() => { this.markHead = this.markHead ? '' : 'arrow'; }))} + onChange={undoable(action(() => { this.markHead = this.markHead ? '' : 'arrow'; }), "change arrow head")} /> </div> <div className="arrows-tail"> @@ -1012,8 +1032,8 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps className="arrows-tail-input" type="checkbox" checked={this.markTail !== ''} - onChange={undoBatch( - action(() => { this.markTail = this.markTail ? '' : 'arrow'; }) + onChange={undoable( + action(() => { this.markTail = this.markTail ? '' : 'arrow'; }) ,"change arrow tail" )} /> </div> @@ -1044,7 +1064,8 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps setFinalNumber = () => { this._sliderBatch?.end(); }; - getNumber = (label: string, unit: string, min: number, max: number, number: number, setNumber: any, autorange?: number, autorangeMinVal?: number) => ( + + getNumber = (label: string, unit: string, min: number, max: number, number: number, setNumber: (val: number) => void, autorange?: number, autorangeMinVal?: number) => ( <div key={label + (this.selectedDoc?.title ?? '')}> <NumberInput formLabel={label} formLabelPlacement="left" type={Type.SEC} unit={unit} fillWidth color={this.color} number={number} setNumber={setNumber} min={min} max={max} /> <Slider @@ -1234,7 +1255,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps } @computed get description() { - return Field.toString(this.selectedLink?.link_description as any as FieldType); + return Field.toString(this.selectedLink?.link_description as FieldType); } @computed get relationship() { return StrCast(this.selectedLink?.link_relationship); @@ -1332,7 +1353,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps <div style={{ ...opts, border: direction === PresEffectDirection.Center ? `solid 2px ${color}` : undefined, borderRadius: '20%', cursor: 'pointer', gridColumn, gridRow, justifySelf: 'center', background: color, color: 'black' }} onClick={() => this.changeEffectDirection(direction)}> - {icon ? <FontAwesomeIcon icon={icon as any} /> : null} + {icon ? <FontAwesomeIcon icon={icon as IconProp} /> : null} </div> </Tooltip> ); @@ -1368,7 +1389,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps e, returnFalse, emptyFunction, - undoBatch(action(() => { this.selectedLink && (this.selectedLink[prop] = !this.selectedLink[prop]); })) // prettier-ignore + undoable(action(() => { this.selectedLink && (this.selectedLink[prop] = !this.selectedLink[prop]); }), `toggle prop: ${prop}`) // prettier-ignore ); }; @@ -1385,17 +1406,17 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps return selAnchor ?? (this.selectedLink && this.destinationAnchor ? Doc.getOppositeAnchor(this.selectedLink, this.destinationAnchor) : this.selectedLink); } - toggleAnchorProp = (e: React.PointerEvent, prop: string, anchor?: Doc, value: any = true, ovalue: any = false, cb: (val: any) => any = val => val) => { + toggleAnchorProp = (e: React.PointerEvent, prop: string, anchor?: Doc, value: FieldType = true, ovalue: FieldType = false, cb: (val: FieldType) => void = val => val) => { anchor && setupMoveUpEvents( this, e, returnFalse, emptyFunction, - undoBatch(action(() => { + undoable(action(() => { anchor[prop] = anchor[prop] === value ? ovalue : value; - this.selectedDoc && cb(anchor[prop]); - })) // prettier-ignore + this.selectedDoc && cb(anchor[prop] as boolean); + }), `toggle anchor prop: ${prop}`) // prettier-ignore ); }; @@ -1433,7 +1454,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps } // Converts seconds to ms and updates presTransition - setZoom = (number: String, change?: number) => { + setZoom = (number: string, change?: number) => { let scale = Number(number) / 100; if (change) scale += change; if (scale < 0.01) scale = 0.01; @@ -1530,7 +1551,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps <div className="propertiesView-input inline"> <p>Play Target Audio</p> { - // eslint-disable-next-line jsx-a11y/control-has-associated-label <button type="button" style={{ background: !this.sourceAnchor?.followLinkAudio ? '' : '#4476f7', borderRadius: 3 }} @@ -1544,7 +1564,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps <div className="propertiesView-input inline"> <p>Play Target Video</p> { - // eslint-disable-next-line jsx-a11y/control-has-associated-label <button type="button" style={{ background: !this.sourceAnchor?.followLinkVideo ? '' : '#4476f7', borderRadius: 3 }} @@ -1558,7 +1577,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps <div className="propertiesView-input inline"> <p>Zoom Text Selections</p> { - // eslint-disable-next-line jsx-a11y/control-has-associated-label <button type="button" style={{ background: !this.sourceAnchor?.followLinkZoomText ? '' : '#4476f7', borderRadius: 3 }} @@ -1572,7 +1590,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps <div className="propertiesView-input inline"> <p>Toggle Follow to Outer Context</p> { - // eslint-disable-next-line jsx-a11y/control-has-associated-label <button type="button" style={{ background: !this.sourceAnchor?.followLinkToOuterContext ? '' : '#4476f7', borderRadius: 3 }} @@ -1586,7 +1603,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps <div className="propertiesView-input inline"> <p>Toggle Target (Show/Hide)</p> { - // eslint-disable-next-line jsx-a11y/control-has-associated-label <button type="button" style={{ background: !this.sourceAnchor?.followLinkToggle ? '' : '#4476f7', borderRadius: 3 }} @@ -1600,7 +1616,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps <div className="propertiesView-input inline"> <p>Ease Transitions</p> { - // eslint-disable-next-line jsx-a11y/control-has-associated-label <button type="button" style={{ background: this.sourceAnchor?.followLinkEase === 'linear' ? '' : '#4476f7', borderRadius: 3 }} @@ -1614,7 +1629,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps <div className="propertiesView-input inline"> <p>Capture Offset to Target</p> { - // eslint-disable-next-line jsx-a11y/control-has-associated-label <button type="button" style={{ background: this.sourceAnchor?.followLinkXoffset === undefined ? '' : '#4476f7', borderRadius: 3 }} @@ -1631,7 +1645,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps <div className="propertiesView-input inline"> <p>Center Target (no zoom)</p> { - // eslint-disable-next-line jsx-a11y/control-has-associated-label <button type="button" style={{ background: this.sourceAnchor?.followLinkZoom ? '' : '#4476f7', borderRadius: 3 }} @@ -1647,16 +1660,15 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}> <input className="presBox-input" style={{ width: '100%', color: SnappingManager.userColor, backgroundColor: SnappingManager.userBackgroundColor }} readOnly type="number" value={zoom} /> <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}> - <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}> + <div className="ribbon-propertyUpDownItem" onClick={undoable(() => this.setZoom(String(zoom), 0.1), 'Zoom out')}> <FontAwesomeIcon icon="caret-up" /> </div> - <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}> + <div className="ribbon-propertyUpDownItem" onClick={undoable(() => this.setZoom(String(zoom), -0.1), 'Zoom in')}> <FontAwesomeIcon icon="caret-down" /> </div> </div> </div> { - // eslint-disable-next-line jsx-a11y/control-has-associated-label <button type="button" style={{ background: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? '' : '#4476f7', borderRadius: 3, gridColumn: 3 }} @@ -1746,8 +1758,8 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps } if (this.isPres && PresBox.Instance) { const selectedItem: boolean = PresBox.Instance.selectedArray.size > 0; - const type = [DocumentType.AUDIO, DocumentType.VID].includes(DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as any as DocumentType) - ? (DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as any as DocumentType) + const type = [DocumentType.AUDIO, DocumentType.VID].includes(DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as DocumentType) + ? (DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as DocumentType) : PresBox.targetRenderedDoc(PresBox.Instance.activeItem)?.type; return ( <div className="propertiesView" style={{ width: this._props.width }}> diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 9b70f1ca7..8f0a35df0 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -1,5 +1,3 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ import { computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -40,7 +38,7 @@ interface ExtraProps { } @observer export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & ExtraProps> { - constructor(props: any) { + constructor(props: FieldViewProps & ExtraProps) { super(props); makeObservable(this); } @@ -85,7 +83,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr }); Doc.SetSelectOnLoad(target); FormattedTextBox.DontSelectInitialText = true; - const link = DocUtils.MakeLink(anchor, target, { link_relationship: 'inline comment:comment on' }); + DocUtils.MakeLink(anchor, target, { link_relationship: 'inline comment:comment on' }); const taggedContent = this.childFilters() .filter(data => data.split(':')[0]) @@ -102,7 +100,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr }); if (!anchor.text) anchor[DocData].text = '-selection-'; - const textLines: any = [ + const textLines: { type: string; attrs: object; content?: unknown[] }[] = [ { type: 'paragraph', attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null }, @@ -222,7 +220,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr pointerEvents: this._props.isContentActive() ? 'all' : undefined, top: this._props.Document.type !== DocumentType.RTF && StrCast(this._props.Document._layout_showTitle) === 'title' ? 15 : 0, right: 0, - background: this._props.styleProvider?.(this._props.Document, this._props, StyleProp.WidgetColor), + background: this._props.styleProvider?.(this._props.Document, this._props, StyleProp.WidgetColor) as string, width: `100%`, height: '100%', }}> diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index b111904f3..2792955a0 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -17,7 +17,7 @@ import { emptyPath } from '../../Utils'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { IsFollowLinkScript } from '../documents/DocUtils'; import { SnappingManager } from '../util/SnappingManager'; -import { undoBatch, UndoManager } from '../util/UndoManager'; +import { undoable, UndoManager } from '../util/UndoManager'; import { TreeSort } from './collections/TreeSort'; import { Colors } from './global/globalEnums'; import { DocumentView, DocumentViewProps } from './nodes/DocumentView'; @@ -105,8 +105,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & const fieldKey = fieldKeyProp ? fieldKeyProp + '_' : isCaption ? 'caption_' : ''; const isInk = () => layoutDoc?._layout_isSvg && !LayoutTemplateString; const lockedPosition = () => doc && BoolCast(doc._lockedPosition); - const titleHeight = () => styleProvider?.(doc, props, StyleProp.TitleHeight); - const backgroundCol = () => styleProvider?.(doc, props, StyleProp.BackgroundColor + ':nonTransparent' + (isNonTransparentLevel + 1)); + const titleHeight = () => styleProvider?.(doc, props, StyleProp.TitleHeight) as number; + const backgroundCol = () => styleProvider?.(doc, props, StyleProp.BackgroundColor + ':nonTransparent' + (isNonTransparentLevel + 1)) as string; const color = () => styleProvider?.(doc, props, StyleProp.Color) as string; const opacity = () => styleProvider?.(doc, props, StyleProp.Opacity); const layoutShowTitle = () => styleProvider?.(doc, props, StyleProp.ShowTitle) as string; @@ -383,11 +383,12 @@ export function DashboardToggleButton(doc: Doc, field: string, onIcon: IconProp, size={Size.XSMALL} color={color} icon={<FontAwesomeIcon icon={doc[field] ? onIcon : offIcon} />} - onClick={undoBatch( + onClick={undoable( action((e: React.MouseEvent) => { e.stopPropagation(); clickFunc ? clickFunc() : (doc[field] = doc[field] ? undefined : true); - }) + }), + 'toggle dashboard feature' )} /> ); diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index cff32a557..e0d0de165 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -48,7 +48,8 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> { .forEach(key => runInAction(() => this._addedKeys.add(key.replace('layout_', '')))); // prettier-ignore } @computed get scriptField() { - const script = ScriptField.MakeScript('docs.map(d => switchView(d, this))', { this: Doc.name }, { docs: this.props.docViews.map(dv => dv.Document) as any }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const script = ScriptField.MakeScript('docs.map(d => switchView(d, this))', { this: Doc.name }, { docs: this.props.docViews.map(dv => dv.Document) as any }); // allow a captured variable for Doc[] since this script isn't being saved to a Doc return script ? () => script : undefined; } diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx index 2d461c0ab..9b71d46ea 100644 --- a/src/client/views/UndoStack.tsx +++ b/src/client/views/UndoStack.tsx @@ -1,5 +1,3 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ import { Tooltip } from '@mui/material'; import { Popup, Type } from 'browndash-components'; import { observer } from 'mobx-react'; diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index c969c39e5..4bec2d963 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -10,7 +10,6 @@ import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; import { ContextMenu } from '../ContextMenu'; -import { ContextMenuProps } from '../ContextMenuItem'; import { StyleProp } from '../StyleProp'; import { DocumentView } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; @@ -135,7 +134,7 @@ export class CollectionCarouselView extends CollectionSubView() { const cm = ContextMenu.Instance; const revealOptions = cm.findByDescription('Filter Flashcards'); - const revealItems: ContextMenuProps[] = revealOptions && 'subitems' in revealOptions ? revealOptions.subitems : []; + const revealItems = revealOptions?.subitems ?? []; revealItems.push({description: 'All', event: () => {this.layoutDoc.filterOp = undefined;}, icon: 'layer-group',}); // prettier-ignore revealItems.push({description: 'Star', event: () => {this.layoutDoc.filterOp = cardMode.STAR;}, icon: 'star',}); // prettier-ignore revealItems.push({description: 'Practice Mode', event: () => {this.layoutDoc.filterOp = cardMode.PRACTICE;}, icon: 'check',}); // prettier-ignore diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 7b9475590..f1b5e4db2 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -19,7 +19,7 @@ import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; -import { undoBatch } from '../../util/UndoManager'; +import { undoBatch, undoable } from '../../util/UndoManager'; import { AntimodeMenu } from '../AntimodeMenu'; import { EditableView } from '../EditableView'; import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider'; @@ -181,7 +181,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu params: ['target', 'source'], title: 'item view', script: 'this.target.childLayoutTemplate = getDocTemplate(this.source?.[0])', - immediate: undoBatch((source: Doc[]) => { + immediate: undoable((source: Doc[]) => { let formatStr = source.length && Cast(source[0].text, RichTextField, null)?.Text; try { formatStr && JSON.parse(formatStr); @@ -196,25 +196,25 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu Doc.SetInPlace(this.target, 'childLayoutString', undefined, true); Doc.SetInPlace(this.target, 'childLayoutTemplate', undefined, true); } - }), + }, ''), initialize: emptyFunction, }; _narrativeCommand = { params: ['target', 'source'], title: 'child click view', script: 'this.target.childClickedOpenTemplateView = getDocTemplate(this.source?.[0])', - immediate: undoBatch((source: Doc[]) => { + immediate: undoable((source: Doc[]) => { source.length && (this.target.childClickedOpenTemplateView = Doc.getDocTemplate(source?.[0])); - }), + }, 'narrative command'), initialize: emptyFunction, }; _contentCommand = { params: ['target', 'source'], title: 'set content', script: 'getProto(this.target).data = copyField(this.source);', - immediate: undoBatch((source: Doc[]) => { + immediate: undoable((source: Doc[]) => { this.target[DocData].data = new List<Doc>(source); - }), + }, ''), initialize: emptyFunction, }; _onClickCommand = { @@ -225,19 +225,19 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu getProto(this.proxy[0]).target = this.target.target; getProto(this.proxy[0]).source = copyField(this.target.source); }}`, - immediate: undoBatch(() => {}), + immediate: undoable(() => {}, ''), initialize: emptyFunction, }; _viewCommand = { params: ['target'], title: 'bookmark view', script: "this.target._freeform_panX = this.target_freeform_panX; this.target._freeform_panY = this['target-freeform_panY']; this.target._freeform_scale = this['target_freeform_scale']; gotoFrame(this.target, this['target-currentFrame']);", - immediate: undoBatch(() => { + immediate: undoable(() => { this.target._freeform_panX = 0; this.target._freeform_panY = 0; this.target._freeform_scale = 1; this.target._currentFrame = this.target._currentFrame === undefined ? undefined : 0; - }), + }, ''), initialize: (button: Doc) => { button['target-panX'] = this.target._freeform_panX; button['target-panY'] = this.target._freeform_panY; @@ -249,18 +249,18 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu params: ['target'], title: 'fit content', script: 'this.target._freeform_fitContentsToBox = !this.target._freeform_fitContentsToBox;', - immediate: undoBatch(() => { + immediate: undoable(() => { this.target._freeform_fitContentsToBox = !this.target._freeform_fitContentsToBox; - }), + }, ''), initialize: emptyFunction, }; _fitContentCommand = { params: ['target'], title: 'toggle clusters', script: 'this.target._freeform_useClusters = !this.target._freeform_useClusters;', - immediate: undoBatch(() => { + immediate: undoable(() => { this.target._freeform_useClusters = !this.target._freeform_useClusters; - }), + }, ''), initialize: emptyFunction, }; _saveFilterCommand = { @@ -268,10 +268,10 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu title: 'save filter', script: `this.target._childFilters = compareLists(this.target_childFilters,this.target._childFilters) ? undefined : copyField(this.target_childFilters); this.target._searchFilterDocs = compareLists(this.target_searchFilterDocs,this.target._searchFilterDocs) ? undefined: copyField(this.target_searchFilterDocs);`, - immediate: undoBatch(() => { + immediate: undoable(() => { this.target._childFilters = undefined; this.target._searchFilterDocs = undefined; - }), + }, ''), initialize: (button: Doc) => { const activeDash = Doc.ActiveDashboard; if (activeDash) { @@ -594,9 +594,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu */ onNumColsChange = (e: React.ChangeEvent<HTMLInputElement>) => { if (e.currentTarget.valueAsNumber > 0) - undoBatch(() => { + undoable(() => { this.document.gridNumCols = e.currentTarget.valueAsNumber; - })(); + }, '')(); }; /** @@ -625,9 +625,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu onIncrementButtonClick = () => { this.clicked = true; this.entered && (this.document.gridNumCols as number)--; - undoBatch(() => { + undoable(() => { this.document.gridNumCols = this.numCols + 1; - })(); + }, '')(); this.entered = false; }; @@ -638,9 +638,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu this.clicked = true; if (this.numCols > 1 && !this.decrementLimitReached) { this.entered && (this.document.gridNumCols as number)++; - undoBatch(() => { + undoable(() => { this.document.gridNumCols = this.numCols - 1; - })(); + }, '')(); if (this.numCols === 1) this.decrementLimitReached = true; } this.entered = false; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index ff316cfbd..0557a9102 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -213,7 +213,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree ContextMenu.Instance.addItem({ description: 'Options...', subitems: layoutItems, icon: 'eye' }); if (!Doc.noviceMode) { const existingOnClick = ContextMenu.Instance.findByDescription('OnClick...'); - const onClicks: ContextMenuProps[] = existingOnClick.subitems ?? []; + const onClicks: ContextMenuProps[] = existingOnClick?.subitems ?? []; onClicks.push({ description: 'Edit onChecked Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.Document, undefined, 'onCheckedClick'), 'edit onCheckedClick'), icon: 'edit' }); !existingOnClick && ContextMenu.Instance.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' }); } @@ -226,7 +226,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree get editableTitle() { return ( <EditableView - contents={this.dataDoc.title} + contents={StrCast(this.dataDoc.title)} display="block" maxHeight={72} height="auto" diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 5c304b4a9..c9ab5f661 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -48,7 +48,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr private reactionDisposer: IReactionDisposer | undefined; @observable _isContentActive: boolean | undefined = undefined; - constructor(props: any) { + constructor(props: CollectionViewProps) { super(props); makeObservable(this); this._annotationKeySuffix = returnEmptyString; @@ -72,7 +72,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr } get collectionViewType(): CollectionViewType | undefined { - const viewField = StrCast(this.layoutDoc._type_collection) as any as CollectionViewType; + const viewField = StrCast(this.layoutDoc._type_collection) as CollectionViewType; if (CollectionView._safeMode) { switch (viewField) { case CollectionViewType.Freeform: @@ -132,7 +132,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr ]; const existingVm = ContextMenu.Instance.findByDescription(category); - const catItems = existingVm && 'subitems' in existingVm ? existingVm.subitems : []; + const catItems = existingVm?.subitems ?? []; catItems.push({ description: 'Add a Perspective...', addDivider: true, noexpand: true, subitems: subItems, icon: 'eye' }); !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: catItems, icon: 'eye' }); } @@ -151,7 +151,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr }); const options = cm.findByDescription('Options...'); - const optionItems = options && 'subitems' in options ? options.subitems : []; + const optionItems = options?.subitems ?? []; !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.Document.forceActive ? 'Select' : 'Force'} Contents Active`, event: () => {this.Document.forceActive = !this.Document.forceActive}, icon: 'project-diagram' }) : null; // prettier-ignore if (this.Document.childLayout instanceof Doc) { optionItems.push({ description: 'View Child Layout', event: () => this._props.addDocTab(this.Document.childLayout as Doc, OpenWhere.addRight), icon: 'project-diagram' }); @@ -165,7 +165,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr if (!Doc.noviceMode && !this.Document.annotationOn && !this._props.hideClickBehaviors) { const existingOnClick = cm.findByDescription('OnClick...'); - const onClicks = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : []; + const onClicks = existingOnClick?.subitems ?? []; const funcs = [ { key: 'onChildClick', name: 'On Child Clicked' }, { key: 'onChildDoubleClick', name: 'On Child Double Clicked' }, @@ -195,7 +195,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr if (!Doc.noviceMode) { const more = cm.findByDescription('More...'); - const moreItems = more && 'subitems' in more ? more.subitems : []; + const moreItems = more?.subitems ?? []; moreItems.push({ description: 'Export Image Hierarchy', icon: 'columns', event: () => ImageUtils.ExportHierarchyToFileSystem(this.Document) }); !more && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'hand-point-right' }); } diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index 2b7de5082..2c7920bdd 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -1,7 +1,8 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ import { Colors } from 'browndash-components'; import { action, runInAction } from 'mobx'; import { aggregateBounds } from '../../../Utils'; -import { Doc, DocListCast, NumListCast, Opt } from '../../../fields/Doc'; +import { Doc, DocListCast, FieldType, NumListCast, Opt } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; @@ -138,7 +139,7 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines' const map: Map<'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'links' | 'like' | 'star' | 'idea' | 'chat' | '1' | '2' | '3' | '4', { waitForRender?: boolean; - checkResult: (doc: Doc) => any; + checkResult: (doc: Doc) => boolean; setDoc: (doc: Doc, dv: DocumentView) => void; }> = new Map([ ['grid', { @@ -220,6 +221,7 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines' }], ]); for (let i = 0; i < 8; i++) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any map.set((i + 1 + '') as any, { checkResult: (doc: Doc) => NumListCast(doc?.cardSort_visibleSortGroups).includes(i), setDoc: (doc: Doc, dv: DocumentView) => { @@ -278,25 +280,25 @@ ScriptingGlobals.add(function cardHasLabel(label: string) { // }); // eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize' | 'alignment', value: any, checkResult?: boolean) { +ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize' | 'alignment', value: string | number, checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; // prettier-ignore - const map: Map<'font'|'fontColor'|'highlight'|'fontSize'|'alignment', { checkResult: () => any; setDoc: () => void;}> = new Map([ + const map: Map<'font'|'fontColor'|'highlight'|'fontSize'|'alignment', { checkResult: () => string | undefined; setDoc: () => void;}> = new Map([ ['font', { checkResult: () => RichTextMenu.Instance?.fontFamily, - setDoc: () => value && RichTextMenu.Instance?.setFontField(value, 'fontFamily'), + setDoc: () => value && RichTextMenu.Instance?.setFontField(value.toString(), 'fontFamily'), }], ['highlight', { checkResult: () => RichTextMenu.Instance?.fontHighlight, - setDoc: () => value && RichTextMenu.Instance?.setFontField(value, 'fontHighlight'), + setDoc: () => value && RichTextMenu.Instance?.setFontField(value.toString(), 'fontHighlight'), }], ['fontColor', { checkResult: () => RichTextMenu.Instance?.fontColor, - setDoc: () => value && RichTextMenu.Instance?.setFontField(value, 'fontColor'), + setDoc: () => value && RichTextMenu.Instance?.setFontField(value.toString(), 'fontColor'), }], ['alignment', { checkResult: () => RichTextMenu.Instance?.textAlign, - setDoc: () => { value && editorView?.state ? RichTextMenu.Instance?.align(editorView, editorView.dispatch, value):(Doc.UserDoc().textAlign = value); }, + setDoc: () => { value && editorView?.state ? RichTextMenu.Instance?.align(editorView, editorView.dispatch, value.toString() as "center"|"left"|"right"):(Doc.UserDoc().textAlign = value); }, }], ['fontSize', { checkResult: () => RichTextMenu.Instance?.fontSize.replace('px', ''), @@ -317,7 +319,7 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh }); type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'elide' | 'underline' | 'left' | 'center' | 'right' | 'vcent' | 'bullet' | 'decimal'; -type attrfuncs = [attrname, { checkResult: () => boolean; toggle?: () => any }]; +type attrfuncs = [attrname, { checkResult: () => boolean; toggle?: () => unknown }]; // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?: boolean) { @@ -438,7 +440,7 @@ function setActiveTool(tool: InkTool | Gestures, keepPrim: boolean, checkResult? if (GestureOverlay.Instance) { GestureOverlay.Instance.KeepPrimitiveMode = keepPrim; } - if (Object.values(Gestures).includes(tool as any)) { + if (Object.values(Gestures).includes(tool as Gestures)) { if (GestureOverlay.Instance.InkShape === tool && !keepPrim) { Doc.ActiveTool = InkTool.None; GestureOverlay.Instance.InkShape = undefined; @@ -450,14 +452,14 @@ function setActiveTool(tool: InkTool | Gestures, keepPrim: boolean, checkResult? if (Doc.UserDoc().ActiveTool === tool) { Doc.ActiveTool = InkTool.None; } else { - if ([InkTool.StrokeEraser, InkTool.RadiusEraser, InkTool.SegmentEraser].includes(tool as any)) { + if ([InkTool.StrokeEraser, InkTool.RadiusEraser, InkTool.SegmentEraser].includes(tool as InkTool)) { Doc.UserDoc().activeEraserTool = tool; } // pen or eraser if (Doc.ActiveTool === tool && !GestureOverlay.Instance.InkShape && !keepPrim) { Doc.ActiveTool = InkTool.None; } else { - Doc.ActiveTool = tool as any; + Doc.ActiveTool = tool as InkTool; GestureOverlay.Instance.InkShape = undefined; } } @@ -476,10 +478,10 @@ ScriptingGlobals.add(function activeEraserTool() { // toggle: Set overlay status of selected document // eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor' | 'eraserWidth', value: any, checkResult?: boolean) { +ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor' | 'eraserWidth', value: string | number, checkResult?: boolean) { const selected = DocumentView.SelectedDocs().lastElement() ?? Doc.UserDoc(); // prettier-ignore - const map: Map<'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor' | 'eraserWidth', { checkResult: () => any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([ + const map: Map<'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor' | 'eraserWidth', { checkResult: () => number|boolean|string|undefined; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([ ['inkMask', { checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected[DocData].stroke_isInkMask) : ActiveIsInkMask())), setInk: (doc: Doc) => { doc[DocData].stroke_isInkMask = !doc.stroke_isInkMask; }, @@ -508,7 +510,7 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fil [ 'eraserWidth', { checkResult: () => ActiveEraserWidth(), setInk: (doc: Doc) => { }, - setMode: () => { SetEraserWidth(value.toString());}, + setMode: () => { SetEraserWidth(+value);}, }] ]); diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index 12b83414c..b38213e08 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -24,7 +24,7 @@ interface Props { export class LinkMenu extends ObservableReactComponent<Props> { _editorRef = React.createRef<HTMLDivElement>(); @observable _linkMenuRef = React.createRef<HTMLDivElement>(); - constructor(props: any) { + constructor(props: Props) { super(props); makeObservable(this); } @@ -40,7 +40,7 @@ export class LinkMenu extends ObservableReactComponent<Props> { onPointerDown = action((e: PointerEvent) => { LinkInfo.Clear(); - if (!this._linkMenuRef.current?.contains(e.target as any) && !this._editorRef.current?.contains(e.target as any)) { + if (!this._linkMenuRef.current?.contains(e.target as HTMLElement) && !this._editorRef.current?.contains(e.target as HTMLElement)) { this.clear(); } }); diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx index cd735318e..c15508669 100644 --- a/src/client/views/linking/LinkMenuGroup.tsx +++ b/src/client/views/linking/LinkMenuGroup.tsx @@ -1,5 +1,3 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ /* eslint-disable react/require-default-props */ import { action, observable } from 'mobx'; import { observer } from 'mobx-react'; diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 9ce04ffac..f54d8311d 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -1,5 +1,3 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; import { action, computed, makeObservable, observable } from 'mobx'; @@ -15,7 +13,7 @@ import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { LinkManager } from '../../util/LinkManager'; import { SnappingManager } from '../../util/SnappingManager'; -import { undoBatch } from '../../util/UndoManager'; +import { undoable } from '../../util/UndoManager'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; import { LinkInfo } from '../nodes/LinkDocPreview'; @@ -56,7 +54,7 @@ export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: Docume export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> { private _drag = React.createRef<HTMLDivElement>(); _editRef = React.createRef<HTMLDivElement>(); - constructor(props: any) { + constructor(props: LinkMenuItemProps) { super(props); makeObservable(this); } @@ -123,7 +121,7 @@ export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> { this, e, moveEv => { - const eleClone: any = this._drag.current?.cloneNode(true); + const eleClone = this._drag.current?.cloneNode(true) as HTMLElement; if (eleClone) { eleClone.style.transform = `translate(${moveEv.x}px, ${moveEv.y}px)`; StartLinkTargetsDrag(eleClone, this._props.docView, moveEv.x, moveEv.y, this._props.sourceDoc, [this._props.linkDoc]); @@ -151,7 +149,17 @@ export class LinkMenuItem extends ObservableReactComponent<LinkMenuItemProps> { ); }; - deleteLink = (e: React.PointerEvent): void => setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => Doc.DeleteLink?.(this._props.linkDoc)))); + deleteLink = (e: React.PointerEvent): void => + setupMoveUpEvents( + this, + e, + returnFalse, + emptyFunction, + undoable( + action(() => Doc.DeleteLink?.(this._props.linkDoc)), + 'delete link' + ) + ); @observable _hover = false; docView = () => this._props.docView; render() { diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 69277b054..df6e74d85 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -429,7 +429,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { specificContextMenu = (): void => { const cm = ContextMenu.Instance; const options = cm.findByDescription('Options...'); - const optionItems = options && 'subitems' in options ? options.subitems : []; + const optionItems = options?.subitems ?? []; optionItems.push({ description: `Analyze with AI`, event: () => this.askGPT(), icon: 'lightbulb' }); !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' }); }; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 8a2c4e530..b178d6554 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -89,8 +89,8 @@ export class DocumentContentsView extends ObservableReactComponent<DocumentConte /** * Set of all available rendering componets for Docs (e.g., ImageBox, CollectionFreeFormView, etc) */ - private static Components: { [key: string]: DocumentContentsViewProps }; - public static Init(defaultLayoutString: string, components: { [key: string]: DocumentContentsViewProps }) { + private static Components: { [key: string]: unknown }; + public static Init(defaultLayoutString: string, components: { [key: string]: unknown }) { DocumentContentsView.DefaultLayoutString = defaultLayoutString; DocumentContentsView.Components = components; } diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx index 3d1bd7563..6b439cd64 100644 --- a/src/client/views/nodes/FunctionPlotBox.tsx +++ b/src/client/views/nodes/FunctionPlotBox.tsx @@ -1,4 +1,4 @@ -import functionPlot from 'function-plot'; +import functionPlot, { Chart } from 'function-plot'; import { computed, makeObservable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -22,11 +22,11 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps> return FieldView.LayoutString(FunctionPlotBox, fieldKey); } public static GraphCount = 0; - _plot: any; + _plot: Chart | undefined; _plotId = ''; - _plotEle: any; + _plotEle: HTMLDivElement | null = null; - constructor(props: any) { + constructor(props: FieldViewProps) { super(props); makeObservable(this); this._plotId = 'graph' + FunctionPlotBox.GraphCount++; @@ -42,8 +42,10 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps> getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document }); PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), datarange: true } }, this.Document); - anchor.config_xRange = new List<number>(Array.from(this._plot.options.xAxis.domain)); - anchor.config_yRange = new List<number>(Array.from(this._plot.options.yAxis.domain)); + if (this._plot) { + anchor.config_xRange = new List<number>(Array.from(this._plot.options.xAxis?.domain ?? [])); + anchor.config_yRange = new List<number>(Array.from(this._plot.options.yAxis?.domain ?? [])); + } if (addAsAnnotation) this.addDocument(anchor); return anchor; }; @@ -68,9 +70,9 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps> const width = this._props.PanelWidth(); const height = this._props.PanelHeight(); try { - this._plotEle.children.length && this._plotEle.removeChild(this._plotEle.children[0]); + this._plotEle?.children.length && this._plotEle.removeChild(this._plotEle.children[0]); this._plot = functionPlot({ - target: '#' + this._plotEle.id, + target: '#' + this._plotEle?.id, width, height, xAxis: { domain: Cast(this.layoutDoc.xRange, listSpec('number'), [-10, 10]) }, @@ -104,7 +106,7 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent<FieldViewProps> return false; }; - _dropDisposer: any; + _dropDisposer: DragManager.DragDropDisposer | undefined; protected createDropTarget = (ele: HTMLDivElement) => { this._dropDisposer?.(); if (ele) { diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 9e77e0973..95e344004 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -14,7 +14,6 @@ import { SetupDrag } from '../../util/DragManager'; import { CompiledScript } from '../../util/Scripting'; import { undoable } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; -import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxBaseComponent } from '../DocComponent'; import { DocumentIconContainer } from './DocumentIcon'; import { FieldView, FieldViewProps } from './FieldView'; @@ -296,7 +295,7 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() { specificContextMenu = (): void => { const cm = ContextMenu.Instance; const open = cm.findByDescription('Change Perspective...'); - const openItems: ContextMenuProps[] = open && 'subitems' in open ? open.subitems : []; + const openItems = open?.subitems ?? []; openItems.push({ description: 'Default Perspective', event: () => { diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 3023716b1..8e74e1ca2 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -110,7 +110,7 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> { <td className="keyValuePair-td-value" style={{ width: `${100 - this._props.keyWidth}%` }} onContextMenu={this.onContextMenu}> <div className="keyValuePair-td-value-container"> <EditableView - contents={undefined} + contents={''} fieldContents={{ Document: this._props.doc, childFilters: returnEmptyFilter, diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index c03694dd9..b17275a1e 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -22,7 +22,6 @@ import { undoBatch, UndoManager } from '../../util/UndoManager'; import { CollectionFreeFormView } from '../collections/collectionFreeForm'; import { CollectionStackingView } from '../collections/CollectionStackingView'; import { ContextMenu } from '../ContextMenu'; -import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { Colors } from '../global/globalEnums'; import { PDFViewer } from '../pdf/PDFViewer'; @@ -398,6 +397,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { </button> </> ); + const searchTitle = `${!this._searching ? 'Open' : 'Close'} Search Bar`; const curPage = NumCast(this.Document._layout_curPage) || 1; return !this._props.isContentActive() || this._pdfViewer?.isAnnotating ? null : ( @@ -473,14 +473,14 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { specificContextMenu = (): void => { const cm = ContextMenu.Instance; const options = cm.findByDescription('Options...'); - const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : []; + const optionItems = options?.subitems ?? []; !Doc.noviceMode && optionItems.push({ description: 'Toggle Sidebar Type', event: this.toggleSidebarType, icon: 'expand-arrows-alt' }); !Doc.noviceMode && optionItems.push({ description: 'update icon', event: () => this.pdfUrl && this.updateIcon(), icon: 'expand-arrows-alt' }); // optionItems.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" }); !options && ContextMenu.Instance.addItem({ description: 'Options...', subitems: optionItems, icon: 'asterisk' }); const help = cm.findByDescription('Help...'); - const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : []; + const helpItems = help?.subitems ?? []; helpItems.push({ description: 'Copy path', event: () => this.pdfUrl && ClientUtils.CopyText(ClientUtils.prepend('') + this.pdfUrl.url.pathname), icon: 'expand-arrows-alt' }); !help && ContextMenu.Instance.addItem({ description: 'Help...', noexpand: true, subitems: helpItems, icon: 'asterisk' }); }; diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx index 4d0e9efee..df1421a33 100644 --- a/src/client/views/nodes/formattedText/EquationView.tsx +++ b/src/client/views/nodes/formattedText/EquationView.tsx @@ -17,7 +17,7 @@ interface IEquationViewInternal { tbox: FormattedTextBox; width: number; height: number; - getPos: () => number; + getPos: () => number | undefined; setEditor: (editor: EquationEditor | undefined) => void; } @@ -47,7 +47,7 @@ export class EquationViewInternal extends React.Component<IEquationViewInternal> className="equationView" onKeyDown={e => { if (e.key === 'Enter') { - this.props.tbox.EditorView!.dispatch(this.props.tbox.EditorView!.state.tr.setSelection(new TextSelection(this.props.tbox.EditorView!.state.doc.resolve(this.props.getPos() + 1)))); + this.props.tbox.EditorView!.dispatch(this.props.tbox.EditorView!.state.tr.setSelection(new TextSelection(this.props.tbox.EditorView!.state.doc.resolve((this.props.getPos() ?? 0) + 1)))); this.props.tbox.EditorView!.focus(); e.preventDefault(); } @@ -82,8 +82,8 @@ export class EquationView { tbox: FormattedTextBox; view: EditorView; _editor: EquationEditor | undefined; - getPos: () => number; - constructor(node: Node, view: EditorView, getPos: () => number, tbox: FormattedTextBox) { + getPos: () => number | undefined; + constructor(node: Node, view: EditorView, getPos: () => number | undefined, tbox: FormattedTextBox) { this.tbox = tbox; this.view = view; this.getPos = getPos; @@ -109,7 +109,7 @@ export class EquationView { this._editor?.mathField.focus(); } selectNode() { - this.view.dispatch(this.view.state.tr.setSelection(new TextSelection(this.view.state.doc.resolve(this.getPos())))); + this.view.dispatch(this.view.state.tr.setSelection(new TextSelection(this.view.state.doc.resolve(this.getPos() ?? 0)))); this.tbox._applyingChange = this.tbox.fieldKey; // setting focus will make prosemirror lose focus, which will cause it to change its selection to a text selection, which causes this view to get rebuilt but it's no longer node selected, so the equationview won't have focus setTimeout(() => { this._editor?.mathField.focus(); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index c8b25e184..478039ffa 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -875,7 +875,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB }) ); const appearance = cm.findByDescription('Appearance...'); - const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : []; + const appearanceItems = appearance?.subitems ?? []; appearanceItems.push({ description: !this.Document._layout_noSidebar ? 'Hide Sidebar Handle' : 'Show Sidebar Handle', @@ -930,7 +930,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB !appearance && appearanceItems.length && cm.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' }); const options = cm.findByDescription('Options...'); - const optionItems = options && 'subitems' in options ? options.subitems : []; + const optionItems = options?.subitems ?? []; optionItems.push({ description: `Toggle auto update from template`, event: () => { @@ -959,7 +959,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB }); !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' }); const help = cm.findByDescription('Help...'); - const helpItems = help && 'subitems' in help ? help.subitems : []; + const helpItems = help?.subitems ?? []; helpItems.push({ description: `show markdown options`, event: () => RTFMarkup.Instance.setOpen(true), icon: <BsMarkdownFill /> }); !help && cm.addItem({ description: 'Help...', subitems: helpItems, icon: 'eye' }); }; |