From b1376d401e709515cee078cc08b05fd3fb89caeb Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 24 Apr 2024 18:12:30 -0400 Subject: completing eslint pass --- src/client/views/animationtimeline/Timeline.tsx | 60 +++--- .../views/animationtimeline/TimelineOverview.tsx | 16 +- .../views/collections/CollectionCalendarView.tsx | 30 ++- .../views/collections/CollectionCarouselView.tsx | 7 +- .../views/collections/CollectionDockingView.tsx | 6 +- .../collections/CollectionNoteTakingViewColumn.tsx | 39 ++-- .../CollectionNoteTakingViewDivider.tsx | 2 +- .../CollectionStackingViewFieldColumn.tsx | 6 +- .../views/collections/CollectionTreeView.tsx | 21 +-- src/client/views/collections/KeyRestrictionRow.tsx | 32 +++- .../CollectionFreeFormBackgroundGrid.tsx | 1 + .../CollectionFreeFormInfoState.tsx | 31 ++-- .../CollectionFreeFormLayoutEngines.tsx | 8 +- .../CollectionFreeFormRemoteCursors.tsx | 72 ++++---- .../collectionFreeForm/MarqueeOptionsMenu.tsx | 12 +- .../collectionGrid/CollectionGridView.tsx | 52 ++++-- .../collectionLinear/CollectionLinearView.tsx | 3 +- .../views/collections/collectionLinear/index.ts | 2 +- .../CollectionMulticolumnView.tsx | 57 ++++-- .../CollectionMultirowView.tsx | 24 ++- .../collectionMulticolumn/MulticolumnResizer.tsx | 24 +-- .../MulticolumnWidthLabel.tsx | 2 +- .../collectionMulticolumn/MultirowHeightLabel.tsx | 3 +- .../collectionMulticolumn/MultirowResizer.tsx | 26 +-- .../collectionSchema/SchemaColumnHeader.tsx | 18 +- .../collections/collectionSchema/SchemaRowBox.tsx | 23 +-- src/client/views/global/globalEnums.tsx | 52 +++--- src/client/views/linking/LinkMenuGroup.tsx | 27 ++- src/client/views/linking/LinkMenuItem.tsx | 10 +- .../views/linking/LinkRelationshipSearch.tsx | 63 ------- src/client/views/nodes/AudioBox.tsx | 11 +- .../views/nodes/DataVizBox/SchemaCSVPopUp.tsx | 62 +++---- .../views/nodes/DataVizBox/components/PieChart.tsx | 197 ++++++++++---------- src/client/views/nodes/DataVizBox/utils/D3Utils.ts | 29 +-- src/client/views/nodes/DocumentLinksButton.tsx | 6 +- src/client/views/nodes/EquationBox.tsx | 11 +- src/client/views/nodes/FaceRectangle.tsx | 10 +- .../views/nodes/FontIconBox/ButtonInterface.ts | 2 +- src/client/views/nodes/FontIconBox/TrailsIcon.tsx | 86 ++++----- src/client/views/nodes/LinkAnchorBox.tsx | 2 +- src/client/views/nodes/LinkBox.tsx | 83 +++++---- src/client/views/nodes/LinkDescriptionPopup.tsx | 33 ++-- src/client/views/nodes/LinkDocPreview.tsx | 64 ++++--- src/client/views/nodes/LoadingBox.tsx | 4 +- .../views/nodes/MapBox/AnimationSpeedIcons.tsx | 39 ++-- src/client/views/nodes/MapBox/AnimationUtility.ts | 57 +++--- .../views/nodes/MapBox/DirectionsAnchorMenu.tsx | 11 +- src/client/views/nodes/MapBox/GeocoderControl.tsx | 7 +- src/client/views/nodes/MapBox/MapAnchorMenu.tsx | 99 +++++----- src/client/views/nodes/MapBox/MapboxApiUtility.ts | 76 ++++---- src/client/views/nodes/MapBox/MarkerIcons.tsx | 3 +- .../views/nodes/MapboxMapBox/MapboxContainer.tsx | 28 +-- src/client/views/nodes/RadialMenu.tsx | 140 +++++--------- src/client/views/nodes/RadialMenuItem.tsx | 4 +- .../views/nodes/RecordingBox/ProgressBar.tsx | 205 +++++++++++---------- .../views/nodes/RecordingBox/RecordingBox.tsx | 17 +- .../views/nodes/RecordingBox/RecordingView.tsx | 41 ++--- src/client/views/nodes/RecordingBox/index.ts | 4 +- src/client/views/nodes/ScriptingBox.tsx | 162 ++++++++-------- src/client/views/nodes/SliderBox-components.tsx | 114 ++++++------ src/client/views/nodes/VideoBox.tsx | 42 +++-- src/client/views/nodes/audio/AudioWaveform.tsx | 8 +- src/client/views/nodes/audio/WaveCanvas.tsx | 12 +- src/client/views/nodes/calendarBox/CalendarBox.tsx | 7 +- .../nodes/formattedText/DashDocCommentView.tsx | 159 ++++++++-------- .../views/nodes/formattedText/DashDocView.tsx | 123 +++++++------ .../views/nodes/formattedText/EquationEditor.tsx | 3 +- .../views/nodes/formattedText/EquationView.tsx | 93 +++++----- .../views/nodes/formattedText/FootnoteView.tsx | 8 +- .../formattedText/FormattedTextBoxComment.tsx | 24 ++- .../views/nodes/formattedText/OrderedListView.tsx | 9 +- .../views/nodes/formattedText/ParagraphNodeSpec.ts | 101 +++++----- .../views/nodes/formattedText/RichTextRules.ts | 15 +- .../views/nodes/formattedText/SummaryView.tsx | 24 +-- .../views/nodes/generativeFill/GenerativeFill.tsx | 21 ++- .../nodes/generativeFill/GenerativeFillButtons.tsx | 10 +- .../GenerativeFillMathHelpers.ts | 8 +- .../generativeFillUtils/ImageHandler.ts | 36 ++-- .../generativeFillUtils/PointerHandler.ts | 18 +- .../views/nodes/importBox/ImportElementBox.tsx | 3 +- src/client/views/nodes/trails/index.ts | 6 +- src/client/views/pdf/AnchorMenu.tsx | 14 +- 82 files changed, 1516 insertions(+), 1564 deletions(-) delete mode 100644 src/client/views/linking/LinkRelationshipSearch.tsx (limited to 'src/client/views') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index f03e316a2..57498f39f 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -47,13 +47,13 @@ import { Track } from './Track'; @observer export class Timeline extends ObservableReactComponent { - //readonly constants + // readonly constants private readonly DEFAULT_TICK_SPACING: number = 50; private readonly MAX_TITLE_HEIGHT = 75; private readonly MAX_CONTAINER_HEIGHT: number = 800; private readonly DEFAULT_TICK_INCREMENT: number = 1000; - //height variables + // height variables private DEFAULT_CONTAINER_HEIGHT: number = 330; private MIN_CONTAINER_HEIGHT: number = 205; @@ -62,7 +62,7 @@ export class Timeline extends ObservableReactComponent { makeObservable(this); } - //react refs + // react refs @observable private _trackbox = React.createRef(); @observable private _titleContainer = React.createRef(); @observable private _timelineContainer = React.createRef(); @@ -70,7 +70,7 @@ export class Timeline extends ObservableReactComponent { @observable private _roundToggleRef = React.createRef(); @observable private _roundToggleContainerRef = React.createRef(); - //boolean vars and instance vars + // boolean vars and instance vars @observable private _currentBarX: number = 0; @observable private _windSpeed: number = 1; @observable private _totalLength: number = 0; @@ -79,11 +79,11 @@ export class Timeline extends ObservableReactComponent { @observable private _containerHeight: number = this.DEFAULT_CONTAINER_HEIGHT; @observable private _tickSpacing = this.DEFAULT_TICK_SPACING; @observable private _tickIncrement = this.DEFAULT_TICK_INCREMENT; - @observable private _time = 100000; //DEFAULT + @observable private _time = 100000; // DEFAULT @observable private _playButton = faPlayCircle; @observable private _titleHeight = 0; - @observable public IsPlaying: boolean = false; //scrubber playing + @observable public IsPlaying: boolean = false; // scrubber playing /** * collection get method. Basically defines what defines collection's children. These will be tracked in the timeline. Do not edit. @@ -97,30 +97,30 @@ export class Timeline extends ObservableReactComponent { return DocListCast(this._props.Document[this._props.fieldKey]); } - /////////lifecycle functions//////////// + /// //////lifecycle functions//////////// @action componentDidMount() { - const relativeHeight = window.innerHeight / 20; //sets height to arbitrary size, relative to innerHeight - this._titleHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //check if relHeight is less than Maxheight. Else, just set relheight to max - this.MIN_CONTAINER_HEIGHT = this._titleHeight + 130; //offset - this.DEFAULT_CONTAINER_HEIGHT = this._titleHeight * 2 + 130; //twice the titleheight + offset + const relativeHeight = window.innerHeight / 20; // sets height to arbitrary size, relative to innerHeight + this._titleHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; // check if relHeight is less than Maxheight. Else, just set relheight to max + this.MIN_CONTAINER_HEIGHT = this._titleHeight + 130; // offset + this.DEFAULT_CONTAINER_HEIGHT = this._titleHeight * 2 + 130; // twice the titleheight + offset if (!this._props.Document.AnimationLength) { - //if animation length did not exist - this._props.Document.AnimationLength = this._time; //set it to default time + // if animation length did not exist + this._props.Document.AnimationLength = this._time; // set it to default time } else { - this._time = NumCast(this._props.Document.AnimationLength); //else, set time to animationlength stored from before + this._time = NumCast(this._props.Document.AnimationLength); // else, set time to animationlength stored from before } - this._totalLength = this._tickSpacing * (this._time / this._tickIncrement); //the entire length of the timeline div (actual div part itself) - this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; //the visible length of the timeline (the length that you current see) - this._visibleStart = this._infoContainer.current!.scrollLeft; //where the div starts - this._props.Document.isATOn = !this._props.Document.isATOn; //turns the boolean on, saying AT (animation timeline) is on + this._totalLength = this._tickSpacing * (this._time / this._tickIncrement); // the entire length of the timeline div (actual div part itself) + this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; // the visible length of the timeline (the length that you current see) + this._visibleStart = this._infoContainer.current!.scrollLeft; // where the div starts + this._props.Document.isATOn = !this._props.Document.isATOn; // turns the boolean on, saying AT (animation timeline) is on this.toggleHandle(); } componentWillUnmount() { - this._props.Document.AnimationLength = this._time; //save animation length + this._props.Document.AnimationLength = this._time; // save animation length } - ///////////////////////////////////////////////// + /// ////////////////////////////////////////////// /** * React Functional Component @@ -148,7 +148,7 @@ export class Timeline extends ObservableReactComponent { pixel <= 0 ? (this._currentBarX = 0) : pixel >= this._totalLength ? (this._currentBarX = this._totalLength) : (this._currentBarX = pixel); }; - //for playing + // for playing onPlay = (e: React.MouseEvent) => { e.stopPropagation(); this.play(); @@ -181,7 +181,7 @@ export class Timeline extends ObservableReactComponent { e.preventDefault(); e.stopPropagation(); if (this._windSpeed < 64) { - //max speed is 32 + // max speed is 32 this._windSpeed = this._windSpeed * 2; } }; @@ -215,7 +215,7 @@ export class Timeline extends ObservableReactComponent { const scrubberbox = this._infoContainer.current!; const left = scrubberbox.getBoundingClientRect().left; const offsetX = Math.round(e.clientX - left) * this._props.ScreenToLocalTransform().Scale; - this.changeCurrentBarX(offsetX + this._visibleStart); //changes scrubber to clicked scrubber position + this.changeCurrentBarX(offsetX + this._visibleStart); // changes scrubber to clicked scrubber position return false; }; @@ -355,12 +355,12 @@ export class Timeline extends ObservableReactComponent { * tool box includes the toggle buttons at the top of the timeline (both editing mode and play mode) */ private timelineToolBox = (scale: number, totalTime: number) => { - const size = 40 * scale; //50 is default + const size = 40 * scale; // 50 is default const iconSize = 25; const width: number = this._props.PanelWidth(); const modeType = this._props.Document.isATOn ? 'Author' : 'Play'; - //decides if information should be omitted because the timeline is very small + // decides if information should be omitted because the timeline is very small // if its less than 950 pixels then it's going to be overlapping let modeString = modeType, overviewString = '', @@ -469,7 +469,7 @@ export class Timeline extends ObservableReactComponent { this._props.Document.isATOn = !this._props.Document.isATOn; if (!BoolCast(this._props.Document.isATOn)) { - //turning on playmode... + // turning on playmode... roundToggle.style.transform = 'translate(0px, 0px)'; roundToggle.style.animationName = 'turnoff'; roundToggleContainer.style.animationName = 'turnoff'; @@ -477,7 +477,7 @@ export class Timeline extends ObservableReactComponent { timelineContainer.style.top = `${-this._containerHeight}px`; this.toPlay(); } else { - //turning on authoring mode... + // turning on authoring mode... roundToggle.style.transform = 'translate(20px, 0px)'; roundToggle.style.animationName = 'turnon'; roundToggleContainer.style.animationName = 'turnon'; @@ -490,8 +490,8 @@ export class Timeline extends ObservableReactComponent { @action.bound changeLengths() { if (this._infoContainer.current) { - this._visibleLength = this._infoContainer.current.getBoundingClientRect().width; //the visible length of the timeline (the length that you current see) - this._visibleStart = this._infoContainer.current.scrollLeft; //where the div starts + this._visibleLength = this._infoContainer.current.getBoundingClientRect().width; // the visible length of the timeline (the length that you current see) + this._visibleStart = this._infoContainer.current.scrollLeft; // where the div starts } } @@ -515,7 +515,7 @@ export class Timeline extends ObservableReactComponent { } } } else { - //TODO: remove undefineds and duplicates + // TODO: remove undefineds and duplicates } }); return longestTime; diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 489c4dcde..7bf685c9e 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/no-unused-prop-types */ import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -32,7 +33,6 @@ export class TimelineOverview extends React.Component { @observable private visibleTime: number = 0; @observable private currentX: number = 0; @observable private visibleStart: number = 0; - private readonly DEFAULT_HEIGHT = 50; private readonly DEFAULT_WIDTH = 300; componentDidMount() { @@ -50,9 +50,9 @@ export class TimelineOverview extends React.Component { ); } - componentWillUnmount = () => { + componentWillUnmount() { this._authoringReaction && this._authoringReaction(); - }; + } @action setOverviewWidth() { @@ -109,7 +109,7 @@ export class TimelineOverview extends React.Component { e.preventDefault(); e.stopPropagation(); const scrubberRef = this._scrubberRef.current!; - const left = scrubberRef.getBoundingClientRect().left; + const { left } = scrubberRef.getBoundingClientRect(); const offsetX = Math.round(e.clientX - left); this.props.changeCurrentBarX((offsetX / this.activeOverviewWidth) * this.props.totalLength + this.props.currentBarX); }; @@ -152,17 +152,17 @@ export class TimelineOverview extends React.Component { const timeline = this.props.isAuthoring ? [
-
, +
,
-
+
, ] : [
-
+
, -
, +
, ]; return (
diff --git a/src/client/views/collections/CollectionCalendarView.tsx b/src/client/views/collections/CollectionCalendarView.tsx index 578ce77a5..43e5a68eb 100644 --- a/src/client/views/collections/CollectionCalendarView.tsx +++ b/src/client/views/collections/CollectionCalendarView.tsx @@ -25,16 +25,13 @@ export class CollectionCalendarView extends CollectionSubView() { removeCalendar = () => {}; - addCalendar = (doc: Doc | Doc[], annotationKey?: string | undefined): boolean => { + addCalendar = (/* doc: Doc | Doc[], annotationKey?: string | undefined */): boolean => // bring up calendar modal with option to create a calendar - return true; - }; + true; _stackRef = React.createRef(); - panelHeight = () => { - return this._props.PanelHeight() - 40; // this should be the height of the stacking view. For now, it's the hieight of the calendar view minus 40 to allow for a title - }; + panelHeight = () => this._props.PanelHeight() - 40; // this should be the height of the stacking view. For now, it's the hieight of the calendar view minus 40 to allow for a title // most recent calendar should come first sortByMostRecentDate = (calendarA: Doc, calendarB: Doc) => { @@ -46,18 +43,18 @@ export class CollectionCalendarView extends CollectionSubView() { if (aFromDate > bFromDate) { return -1; // a comes first - } else if (aFromDate < bFromDate) { + } + if (aFromDate < bFromDate) { + return 1; // b comes first + } + // start dates are the same + if (aToDate > bToDate) { + return -1; // a comes first + } + if (aToDate < bToDate) { return 1; // b comes first - } else { - // start dates are the same - if (aToDate > bToDate) { - return -1; // a comes first - } else if (aToDate < bToDate) { - return 1; // b comes first - } else { - return 0; // same start and end dates - } } + return 0; // same start and end dates }; screenToLocalTransform = () => @@ -74,6 +71,7 @@ export class CollectionCalendarView extends CollectionSubView() { return (
- +
- +
); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 9a1781b93..fefaf6591 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -120,7 +120,7 @@ export class CollectionDockingView extends CollectionSubView() { @undoBatch public static CloseSplit(document: Opt, panelName?: string): boolean { if (CollectionDockingView.Instance) { - const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find(tab => (panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document)); + const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find(tabView => (panelName ? tabView.contentItem.config.props.panelName === panelName : tabView.DashDoc === document)); if (tab) { const j = tab.header.parent.contentItems.indexOf(tab.contentItem); if (j !== -1) { @@ -147,7 +147,7 @@ export class CollectionDockingView extends CollectionSubView() { stack.contentItems[activeContentItemIndex].remove(); return instance.layoutChanged(); } - const tab = Array.from(instance.tabMap.keys()).find(tab => tab.contentItem.config.props.panelName === panelName); + const tab = Array.from(instance.tabMap.keys()).find(tabView => tabView.contentItem.config.props.panelName === panelName); if (tab) { const j = tab.header.parent.contentItems.indexOf(tab.contentItem); if (newConfig.props.documentId !== tab.header.parent.contentItems[j].config.props.documentId) { @@ -172,7 +172,7 @@ export class CollectionDockingView extends CollectionSubView() { public static AddSplit(document: Doc, pullSide: OpenWhereMod, stack?: any, panelName?: string, keyValue?: boolean) { if (document?._type_collection === CollectionViewType.Docking && !keyValue) return DashboardView.openDashboard(document); if (!CollectionDockingView.Instance) return false; - const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document && !tab.contentItem.config.props.keyValue && !keyValue); + const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tabView => tabView.DashDoc === document && !tabView.contentItem.config.props.keyValue && !keyValue); if (tab) { tab.header.parent.setActiveContentItem(tab.contentItem); return true; diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx index e00fdb50c..95aecc7d0 100644 --- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx +++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx @@ -1,3 +1,4 @@ +/* eslint-disable jsx-a11y/control-has-associated-label */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; @@ -14,7 +15,7 @@ import { DocUtils, Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; -import { undoBatch } from '../../util/UndoManager'; +import { undoBatch, undoable } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { EditableView } from '../EditableView'; @@ -113,13 +114,15 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent { + headingChanged = (value: string /* , shiftDown?: boolean */) => { const castedValue = this.getValue(value); if (castedValue) { if (this._props.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) { return false; } - this._props.docList.forEach(d => (d[this._props.pivotField] = castedValue)); + this._props.docList.forEach(d => { + d[this._props.pivotField] = castedValue; + }); if (this._props.headingObject) { this._props.headingObject.setHeading(castedValue.toString()); this._heading = this._props.headingObject.heading; @@ -129,10 +132,14 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent (this._hover = true); - @action pointerLeave = () => (this._hover = false); - @undoBatch - addTextNote = (char: string) => this.addNewTextDoc('-typed text-', false, true); + @action pointerEntered = () => { + this._hover = true; + }; + @action pointerLeave = () => { + this._hover = false; + }; + + addTextNote = undoable(() => this.addNewTextDoc('-typed text-', false, true), 'add text note'); // addNewTextDoc is called when a user starts typing in a column to create a new node @action @@ -195,6 +202,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent - evContents} isEditingCallback={isEditing => isEditing && this._props.select(false)} SetValue={this.headingChanged} contents={evContents} oneLine={true} /> + evContents} isEditingCallback={isEditing => isEditing && this._props.select(false)} SetValue={this.headingChanged} contents={evContents} oneLine />
{(this._props.colHeaderData?.length ?? 0) > 1 && ( - )}
) : null; const templatecols = this.columnWidth; - const type = this._props.Document.type; return ( <> {headingView} @@ -270,7 +278,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent
- +
- + { + // eslint-disable-next-line react/jsx-props-no-spreading + + }
) : null} diff --git a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx index 67070b24d..ddd4b8137 100644 --- a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx +++ b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx @@ -28,7 +28,7 @@ export class CollectionNoteTakingViewDivider extends ObservableReactComponent { + (moveEv, down, delta) => { if (!batch) batch = UndoManager.StartBatch('resizing'); this._props.setColumnStartXCoords(delta[0], this._props.index); return false; diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 35f330b44..cb463077c 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -157,7 +157,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent< const key = this._props.pivotField; const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _layout_fitWidth: true, title: value, _layout_autoHeight: true }); key && (newDoc[key] = this.getValue(this._props.heading)); - const maxHeading = this._props.docList.reduce((maxHeading, doc) => (NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading), 0); + const maxHeading = this._props.docList.reduce((prevHeading, doc) => (NumCast(doc.heading) > prevHeading ? NumCast(doc.heading) : prevHeading), 0); const heading = maxHeading === 0 || this._props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3; newDoc.heading = heading; FormattedTextBox.SetSelectOnLoad(newDoc); @@ -220,9 +220,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent< renderMenu = () => (
-
{})}> - Add options here -
+
Add options here
); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 538a6fd5e..bf81bdc7f 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -177,24 +177,24 @@ export class CollectionTreeView extends CollectionSubView this.ScreenToLocalBoxXf().translate(0, -this._headerHeight); @action - remove = (doc: Doc | Doc[]): boolean => { - const docs = doc instanceof Doc ? [doc] : doc; + remove = (docIn: Doc | Doc[]): boolean => { + const docs = docIn instanceof Doc ? [docIn] : docIn; const targetDataDoc = this.Document[DocData]; const value = DocListCast(targetDataDoc[this._props.fieldKey]); const result = value.filter(v => !docs.includes(v)); - if ((doc instanceof Doc ? [doc] : doc).some(doc => SelectionManager.Views.some(dv => Doc.AreProtosEqual(dv.Document, doc)))) SelectionManager.DeselectAll(); + if (docs.some(doc => SelectionManager.Views.some(dv => Doc.AreProtosEqual(dv.Document, doc)))) SelectionManager.DeselectAll(); if (result.length !== value.length) { - if (doc instanceof Doc) { - const ind = DocListCast(targetDataDoc[this._props.fieldKey]).indexOf(doc); + if (docIn instanceof Doc) { + const ind = DocListCast(targetDataDoc[this._props.fieldKey]).indexOf(docIn); const prev = ind && DocListCast(targetDataDoc[this._props.fieldKey])[ind - 1]; - this._props.removeDocument?.(doc); + this._props.removeDocument?.(docIn); if (ind > 0 && prev) { FormattedTextBox.SetSelectOnLoad(prev); DocumentManager.Instance.getDocumentView(prev, this.DocumentView?.())?.select(false); } return true; } - return this._props.removeDocument?.(doc) ?? false; + return this._props.removeDocument?.(docIn) ?? false; } return false; }; @@ -299,7 +299,7 @@ export class CollectionTreeView extends CollectionSubView this.addDoc(doc, relativeTo, before); + const treeAddDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before); const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this._props.moveDocument?.(d, target, addDoc) || false; if (this._renderCount < this.treeChildren.length) setTimeout( @@ -315,7 +315,7 @@ export class CollectionTreeView extends CollectionSubView this.addDocument(doc, `${this._props.fieldKey}_annotations`) || false; remAnnotationDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, `${this._props.fieldKey}_annotations`) || false; - moveAnnotationDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) => - this.moveDocument(doc, targetCollection, addDocument, `${this._props.fieldKey}_annotations`) || false; + moveAnnotationDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) => this.moveDocument(doc, targetCollection, addDocument) || false; @observable _headerHeight = 0; @computed get content() { diff --git a/src/client/views/collections/KeyRestrictionRow.tsx b/src/client/views/collections/KeyRestrictionRow.tsx index 4523a4f1e..7dc08389b 100644 --- a/src/client/views/collections/KeyRestrictionRow.tsx +++ b/src/client/views/collections/KeyRestrictionRow.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/button-has-type */ import { observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -35,11 +36,36 @@ export default class KeyRestrictionRow extends React.Component - runInAction(() => (this._key = e.target.value))} placeholder="KEY" /> - - runInAction(() => (this._value = e.target.value))} placeholder="VALUE" /> + + runInAction(() => { + this._value = e.target.value; + }) + } + placeholder="VALUE" + />
); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx index 0acc99360..d2ce17f99 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx @@ -10,6 +10,7 @@ export interface CollectionFreeFormViewBackgroundGridProps { PanelWidth: () => number; PanelHeight: () => number; color: () => string; + // eslint-disable-next-line react/require-default-props isAnnotationOverlay?: boolean; nativeDimScaling: () => number; zoomScaling: () => number; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx index 73dd7fea3..fc39cafaa 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx @@ -5,13 +5,13 @@ import * as React from 'react'; import { SettingsManager } from '../../../util/SettingsManager'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import './CollectionFreeFormView.scss'; -import { Doc } from '../../../../fields/Doc'; /** * An Fsa Arc. The first array element is a test condition function that will be observed. * The second array element is a function that will be invoked when the first test function * returns a truthy value */ +// eslint-disable-next-line no-use-before-define export type infoArc = [() => any, (res?: any) => infoState]; export const StateMessage = Symbol('StateMessage'); @@ -46,6 +46,7 @@ export function InfoState( gif?: string, entryFunc?: () => any ) { + // eslint-disable-next-line new-cap return new infoState(msg, arcs, gif, entryFunc); } @@ -73,14 +74,15 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent this._disposers.map(disposer => disposer()); - initState = () => (this._disposers = - this.Arcs.map(arc => ({ test: arc[0], act: arc[1] })).map( - arc => reaction( - arc.test, - res => res && this._props.next(arc.act(res)), - { fireImmediately: true } - ) - )); // prettier-ignore + initState = () => { + this._disposers = this.Arcs + .map(arc => ({ test: arc[0], act: arc[1] })) + .map(arc => reaction( + arc.test, + res => res && this._props.next(arc.act(res)), + { fireImmediately: true } + ) + )}; // prettier-ignore componentDidMount() { this.initState(); @@ -97,10 +99,15 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent +

{this.State?.[StateMessage]} -

@@ -108,7 +115,7 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent
- this.props.close())} /> + this.props.close())} />
); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 3970c6807..a4496a417 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -147,10 +147,10 @@ export function computePivotLayout(poolData: Map, pivotDoc: Do } const val = Field.toString(pair.layout[pivotFieldKey] as FieldType); if (listValue) { - listValue.forEach((val, i) => { - !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] }); - pivotColumnGroups.get(val)!.docs.push(pair.layout); - pivotColumnGroups.get(val)!.replicas.push(i.toString()); + listValue.forEach((lval, i) => { + !pivotColumnGroups.get(lval) && pivotColumnGroups.set(lval, { docs: [], filters: [lval], replicas: [] }); + pivotColumnGroups.get(lval)!.docs.push(pair.layout); + pivotColumnGroups.get(lval)!.replicas.push(i.toString()); }); } else if (val) { !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx index 35394016d..f64c6715b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx @@ -4,9 +4,7 @@ import * as mobxUtils from 'mobx-utils'; import * as React from 'react'; import * as uuid from 'uuid'; import CursorField from '../../../../fields/CursorField'; -import { Doc, FieldResult } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; -import { List } from '../../../../fields/List'; import { listSpec } from '../../../../fields/Schema'; import { Cast } from '../../../../fields/Types'; import { CollectionViewProps } from '../CollectionSubView'; @@ -16,14 +14,12 @@ import './CollectionFreeFormView.scss'; export class CollectionFreeFormRemoteCursors extends React.Component { @computed protected get cursors(): CursorField[] { const { Document } = this.props; - - let cursors: FieldResult>; - const id = Doc.UserDoc()[Id]; - if (!id || !(cursors = Cast(Document.cursors, listSpec(CursorField)))) { + const cursors = Cast(Document.cursors, listSpec(CursorField)); + if (!cursors) { return []; } const now = mobxUtils.now(); - return (cursors || []).filter(({ data: { metadata } }) => metadata.id !== id && now - metadata.timestamp < 1000); + return (cursors || []).filter(({ data: { metadata } }) => metadata.id !== Document[Id] && now - metadata.timestamp < 1000); } @computed get renderedCursors() { @@ -33,46 +29,44 @@ export class CollectionFreeFormRemoteCursors extends React.Component { - return ( -
- { - if (el) { - const ctx = el.getContext('2d'); - if (ctx) { - ctx.fillStyle = '#' + uuid.v5(metadata.id, uuid.v5.URL).substring(0, 6).toUpperCase() + '22'; - ctx.fillRect(0, 0, 20, 20); + }) => ( +
+ { + if (el) { + const ctx = el.getContext('2d'); + if (ctx) { + ctx.fillStyle = '#' + uuid.v5(metadata.id, uuid.v5.URL).substring(0, 6).toUpperCase() + '22'; + ctx.fillRect(0, 0, 20, 20); - ctx.fillStyle = 'black'; - ctx.lineWidth = 0.5; + ctx.fillStyle = 'black'; + ctx.lineWidth = 0.5; - ctx.beginPath(); + ctx.beginPath(); - ctx.moveTo(10, 0); - ctx.lineTo(10, 8); + ctx.moveTo(10, 0); + ctx.lineTo(10, 8); - ctx.moveTo(10, 20); - ctx.lineTo(10, 12); + ctx.moveTo(10, 20); + ctx.lineTo(10, 12); - ctx.moveTo(0, 10); - ctx.lineTo(8, 10); + ctx.moveTo(0, 10); + ctx.lineTo(8, 10); - ctx.moveTo(20, 10); - ctx.lineTo(12, 10); + ctx.moveTo(20, 10); + ctx.lineTo(12, 10); - ctx.stroke(); - } + ctx.stroke(); } - }} - width={20} - height={20} - /> -

{metadata.identifier[0].toUpperCase()}

-
- ); - } + } + }} + width={20} + height={20} + /> +

{metadata.identifier[0].toUpperCase()}

+
+ ) ); } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx index 79cc534dc..adac5a102 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx @@ -9,6 +9,7 @@ import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; @observer export class MarqueeOptionsMenu extends AntimodeMenu { + // eslint-disable-next-line no-use-before-define static Instance: MarqueeOptionsMenu; public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean) => void = unimplementedFunction; @@ -29,14 +30,13 @@ export class MarqueeOptionsMenu extends AntimodeMenu { } render() { - const presPinWithViewIcon = ; const buttons = ( <> - } color={this.userColor} /> - this.createCollection(e, true)} icon={} color={this.userColor} /> - } color={this.userColor} /> - } color={this.userColor} /> - } color={this.userColor} /> + } color={this.userColor} /> + this.createCollection(e, true)} icon={} color={this.userColor} /> + } color={this.userColor} /> + } color={this.userColor} /> + } color={this.userColor} /> ); return this.getElement(buttons); diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index ab6788e6f..1634daaf7 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -17,6 +17,7 @@ import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; import { CollectionSubView } from '../CollectionSubView'; import './CollectionGridView.scss'; import Grid, { Layout } from './Grid'; + @observer export class CollectionGridView extends CollectionSubView() { private _containerRef: React.RefObject = React.createRef(); @@ -77,15 +78,13 @@ export class CollectionGridView extends CollectionSubView() { pairs.forEach((pair, i) => { const existing = oldLayouts.find(l => l.i === pair.layout[Id]); if (existing) newLayouts.push(existing); - else { - if (Object.keys(this.dropLocation).length) { - // external drop - this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.dropLocation as { x: number; y: number }, !this.flexGrid)); - this.dropLocation = {}; - } else { - // internal drop - this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.unflexedPosition(i), !this.flexGrid)); - } + else if (Object.keys(this.dropLocation).length) { + // external drop + this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.dropLocation as { x: number; y: number }, !this.flexGrid)); + this.dropLocation = {}; + } else { + // internal drop + this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.unflexedPosition(i), !this.flexGrid)); } }); pairs?.length && this.setLayoutList(newLayouts); @@ -140,9 +139,7 @@ export class CollectionGridView extends CollectionSubView() { /** * Creates a layout object for a grid item */ - makeLayoutItem = (doc: Doc, pos: { x: number; y: number }, Static: boolean = false, w: number = this.defaultW, h: number = this.defaultH) => { - return { i: doc[Id], w, h, x: pos.x, y: pos.y, static: Static }; - }; + makeLayoutItem = (doc: Doc, pos: { x: number; y: number }, Static: boolean = false, w: number = this.defaultW, h: number = this.defaultH) => ({ i: doc[Id], w, h, x: pos.x, y: pos.y, static: Static }); /** * Adds a layout to the list of layouts. @@ -190,6 +187,7 @@ export class CollectionGridView extends CollectionSubView() { getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { return ( { - const gridLayout = savedLayouts.find(gridLayout => gridLayout.i === doc[Id]); + const gridLayout = savedLayouts.find(layout => layout.i === doc[Id]); if (gridLayout) Object.assign(gridLayout, layoutArray.find(layout => layout.i === doc[Id]) || gridLayout); }); @@ -318,7 +316,9 @@ export class CollectionGridView extends CollectionSubView() { e, returnFalse, action(() => { - undoBatch(() => (this.Document.gridRowHeight = this._rowHeight))(); + undoBatch(() => { + this.Document.gridRowHeight = this._rowHeight; + })(); this._rowHeight = undefined; }), emptyFunction, @@ -332,8 +332,20 @@ export class CollectionGridView extends CollectionSubView() { */ onContextMenu = () => { const displayOptionsMenu: ContextMenuProps[] = []; - displayOptionsMenu.push({ description: 'Toggle Content Display Style', event: () => (this.Document.display = this.Document.display ? undefined : 'contents'), icon: 'copy' }); - displayOptionsMenu.push({ description: 'Toggle Vertical Centering', event: () => (this.Document.centerY = !this.Document.centerY), icon: 'copy' }); + displayOptionsMenu.push({ + description: 'Toggle Content Display Style', + event: () => { + this.Document.display = this.Document.display ? undefined : 'contents'; + }, + icon: 'copy', + }); + displayOptionsMenu.push({ + description: 'Toggle Vertical Centering', + event: () => { + this.Document.centerY = !this.Document.centerY; + }, + icon: 'copy', + }); ContextMenu.Instance.addItem({ description: 'Display', subitems: displayOptionsMenu, icon: 'tv' }); }; @@ -347,14 +359,14 @@ export class CollectionGridView extends CollectionSubView() { e, returnFalse, returnFalse, - (e: PointerEvent, doubleTap?: boolean) => { - if (doubleTap && !e.button) { + (clickEv: PointerEvent, doubleTap?: boolean) => { + if (doubleTap && !clickEv.button) { undoBatch( action(() => { const text = Docs.Create.TextDocument('', { _width: 150, _height: 50 }); FormattedTextBox.SetSelectOnLoad(text); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed Doc.AddDocToList(this.Document, this._props.fieldKey, text); - this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(e.clientX, e.clientY)))); + this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(clickEv.clientX, clickEv.clientY)))); }) )(); } @@ -387,7 +399,7 @@ export class CollectionGridView extends CollectionSubView() { width={this._props.PanelWidth()} nodeList={this.contents.length ? this.contents : null} layout={this.contents.length ? this.renderedLayoutList : undefined} - childrenDraggable={this._props.isSelected() ? true : false} + childrenDraggable={!!this._props.isSelected()} numCols={this.numCols} rowHeight={this.rowHeight} setLayout={this.setLayout} diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index 6635ab222..e68ed0e17 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -15,7 +15,8 @@ import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../ import { CollectionViewType } from '../../../documents/DocumentTypes'; import { BranchingTrailManager } from '../../../util/BranchingTrailManager'; import { DocumentManager } from '../../../util/DocumentManager'; -import { DragManager, dropActionType } from '../../../util/DragManager'; +import { DragManager } from '../../../util/DragManager'; +import { dropActionType } from '../../../util/DropActionTypes'; import { SettingsManager } from '../../../util/SettingsManager'; import { Transform } from '../../../util/Transform'; import { UndoStack } from '../../UndoStack'; diff --git a/src/client/views/collections/collectionLinear/index.ts b/src/client/views/collections/collectionLinear/index.ts index ff73e14ae..ab3b4b0b5 100644 --- a/src/client/views/collections/collectionLinear/index.ts +++ b/src/client/views/collections/collectionLinear/index.ts @@ -1 +1 @@ -export * from "./CollectionLinearView"; \ No newline at end of file +export * from './CollectionLinearView'; diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 125dd2781..b8509a005 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -1,3 +1,5 @@ +/* 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 { Button, IconButton } from 'browndash-components'; @@ -7,7 +9,7 @@ import * as React from 'react'; import { FaChevronRight } from 'react-icons/fa'; import { Doc, DocListCast } from '../../../../fields/Doc'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; -import { DragManager, dropActionType } from '../../../util/DragManager'; +import { DragManager } from '../../../util/DragManager'; import { SettingsManager } from '../../../util/SettingsManager'; import { Transform } from '../../../util/Transform'; import { undoBatch, undoable } from '../../../util/UndoManager'; @@ -16,6 +18,7 @@ import { CollectionSubView } from '../CollectionSubView'; import './CollectionMulticolumnView.scss'; import ResizeBar from './MulticolumnResizer'; import WidthLabel from './MulticolumnWidthLabel'; +import { dropActionType } from '../../../util/DropActionTypes'; interface WidthSpecifier { magnitude: number; @@ -80,7 +83,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { private get resolvedLayoutInformation(): LayoutData { let starSum = 0; const widthSpecifiers: WidthSpecifier[] = []; - this.childLayouts.map(layout => { + this.childLayouts.forEach(layout => { const unit = StrCast(layout._dimUnit, '*'); const magnitude = NumCast(layout._dimMagnitude, this.minimumDim); if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) { @@ -140,6 +143,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { if (layoutInfoLen > 0 && this.totalFixedAllocation !== undefined) { return this._props.PanelWidth() - (this.totalFixedAllocation + resizerWidth * (layoutInfoLen - 1)) - 2 * NumCast(this.Document._xMargin); } + return undefined; } /** @@ -159,6 +163,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { if (this.resolvedLayoutInformation && this.totalRatioAllocation !== undefined) { return this.totalRatioAllocation / this.resolvedLayoutInformation.starSum; } + return undefined; } /** @@ -175,7 +180,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { * or the ratio width evaluated to a pixel value */ private lookupPixels = (layout: Doc): number => { - const columnUnitLength = this.columnUnitLength; + const { columnUnitLength } = this; if (columnUnitLength === undefined) { return 0; // we're still waiting on promises to resolve } @@ -193,19 +198,19 @@ export class CollectionMulticolumnView extends CollectionSubView() { * documents before the target. */ private lookupIndividualTransform = (layout: Doc) => { - const columnUnitLength = this.columnUnitLength; + const { columnUnitLength } = this; if (columnUnitLength === undefined) { return Transform.Identity(); // we're still waiting on promises to resolve } let offset = 0; - var xf = Transform.Identity(); - this.childLayouts.map(candidate => { + // eslint-disable-next-line no-restricted-syntax + for (const { layout: candidate } of this.childLayoutPairs) { if (candidate === layout) { - return (xf = this.ScreenToLocalBoxXf().translate(-offset / (this._props.NativeDimScaling?.() || 1), 0)); + return this.ScreenToLocalBoxXf().translate(0, -offset / (this._props.NativeDimScaling?.() || 1)); } offset += this.lookupPixels(candidate) + resizerWidth; - }); - return xf; + } + return Transform.Identity(); }; @undoBatch @@ -213,7 +218,9 @@ export class CollectionMulticolumnView extends CollectionSubView() { let dropInd = -1; if (de.complete.docDragData && this._mainCont) { let curInd = -1; - de.complete.docDragData?.droppedDocuments.forEach(d => (curInd = this.childDocs.indexOf(d))); + de.complete.docDragData?.droppedDocuments.forEach(d => { + curInd = this.childDocs.indexOf(d); + }); Array.from(this._mainCont.children).forEach((child, index) => { const brect = child.getBoundingClientRect(); if (brect.x < de.x && brect.x + brect.width > de.x) { @@ -265,7 +272,6 @@ export class CollectionMulticolumnView extends CollectionSubView() { this.lookupIndividualTransform(childLayout) .translate(-NumCast(this.layoutDoc._xMargin), -NumCast(this.layoutDoc._yMargin)) .scale(this._props.NativeDimScaling?.() || 1); - const shouldNotScale = () => this._props.fitContentsToBox?.() || BoolCast(childLayout.freeform_fitContentsToBox); return ( { collector.push( + // eslint-disable-next-line react/no-array-index-key
{this.getDisplayDoc(layout)} {this.layoutDoc._chromeHidden ? null : ( -
, -
(this._startIndex = Math.min(this.childLayoutPairs.length - 1, this._startIndex + this.maxShown)))}> -
)} {this._startIndex > this.childLayoutPairs.length - 1 || !this.maxShown ? null : ( -
(this._startIndex = Math.min(this.childLayoutPairs.length - 1, this._startIndex + this.maxShown)))}> +
{ + this._startIndex = Math.min(this.childLayoutPairs.length - 1, this._startIndex + this.maxShown); + })}> } color={SettingsManager.userColor} />
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 17bf3e50c..3fe3d5343 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -3,7 +3,8 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast } from '../../../../fields/Doc'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; -import { DragManager, dropActionType } from '../../../util/DragManager'; +import { DragManager } from '../../../util/DragManager'; +import { dropActionType } from '../../../util/DropActionTypes'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; import { DocumentView } from '../../nodes/DocumentView'; @@ -11,6 +12,7 @@ import { CollectionSubView } from '../CollectionSubView'; import './CollectionMultirowView.scss'; import HeightLabel from './MultirowHeightLabel'; import ResizeBar from './MultirowResizer'; + interface HeightSpecifier { magnitude: number; unit: string; @@ -63,7 +65,7 @@ export class CollectionMultirowView extends CollectionSubView() { private get resolvedLayoutInformation(): LayoutData { let starSum = 0; const heightSpecifiers: HeightSpecifier[] = []; - this.childLayoutPairs.map(pair => { + this.childLayoutPairs.forEach(pair => { const unit = StrCast(pair.layout._dimUnit, '*'); const magnitude = NumCast(pair.layout._dimMagnitude, this.minimumDim); if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) { @@ -123,6 +125,7 @@ export class CollectionMultirowView extends CollectionSubView() { if (layoutInfoLen > 0 && this.totalFixedAllocation !== undefined) { return this._props.PanelHeight() - (this.totalFixedAllocation + resizerHeight * (layoutInfoLen - 1)) - 2 * NumCast(this.Document._yMargin); } + return undefined; } /** @@ -142,6 +145,7 @@ export class CollectionMultirowView extends CollectionSubView() { if (this.resolvedLayoutInformation && this.totalRatioAllocation !== undefined) { return this.totalRatioAllocation / this.resolvedLayoutInformation.starSum; } + return undefined; } /** @@ -158,13 +162,12 @@ export class CollectionMultirowView extends CollectionSubView() { * or the ratio width evaluated to a pixel value */ private lookupPixels = (layout: Doc): number => { - const rowUnitLength = this.rowUnitLength; - if (rowUnitLength === undefined) { + if (this.rowUnitLength === undefined) { return 0; // we're still waiting on promises to resolve } let height = NumCast(layout._dimMagnitude, this.minimumDim); if (StrCast(layout._dimUnit, '*') === DimUnit.Ratio) { - height *= rowUnitLength; + height *= this.rowUnitLength; } return height; }; @@ -176,11 +179,11 @@ export class CollectionMultirowView extends CollectionSubView() { * documents before the target. */ private lookupIndividualTransform = (layout: Doc) => { - const rowUnitLength = this.rowUnitLength; - if (rowUnitLength === undefined) { + if (this.rowUnitLength === undefined) { return Transform.Identity(); // we're still waiting on promises to resolve } let offset = 0; + // eslint-disable-next-line no-restricted-syntax for (const { layout: candidate } of this.childLayoutPairs) { if (candidate === layout) { return this.ScreenToLocalBoxXf().translate(0, -offset / (this._props.NativeDimScaling?.() || 1)); @@ -195,7 +198,9 @@ export class CollectionMultirowView extends CollectionSubView() { let dropInd = -1; if (de.complete.docDragData && this._mainCont) { let curInd = -1; - de.complete.docDragData?.droppedDocuments.forEach(d => (curInd = this.childDocs.indexOf(d))); + de.complete.docDragData?.droppedDocuments.forEach(d => { + curInd = this.childDocs.indexOf(d); + }); Array.from(this._mainCont.children).forEach((child, index) => { const brect = child.getBoundingClientRect(); if (brect.y < de.y && brect.y + brect.height > de.y) { @@ -247,7 +252,6 @@ export class CollectionMultirowView extends CollectionSubView() { this.lookupIndividualTransform(layout) .translate(-NumCast(this.layoutDoc._xMargin), -NumCast(this.layoutDoc._yMargin)) .scale(this._props.NativeDimScaling?.() || 1); - const shouldNotScale = () => this._props.fitContentsToBox?.() || BoolCast(layout.freeform_fitContentsToBox); return ( { } }; - private get isActivated() { - const { toLeft, toRight } = this.props; - if (toLeft && toRight) { - if (StrCast(toLeft._dimUnit, '*') === DimUnit.Pixel && StrCast(toRight._dimUnit, '*') === DimUnit.Pixel) { - return false; - } - return true; - } else if (toLeft) { - if (StrCast(toLeft._dimUnit, '*') === DimUnit.Pixel) { - return false; - } - return true; - } else if (toRight) { - if (StrCast(toRight._dimUnit, '*') === DimUnit.Pixel) { - return false; - } - return true; - } - return false; - } - @action private onPointerUp = () => { window.removeEventListener('pointermove', this.onPointerMove); @@ -90,7 +70,7 @@ export default class ResizeBar extends React.Component { width: this.props.width, backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor), }}> -
this.registerResizing(e)} /> +
this.registerResizing(e)} />
); } diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx index a9579d931..a7a0b3457 100644 --- a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx +++ b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx @@ -19,7 +19,7 @@ export default class WidthLabel extends React.Component { const getUnit = () => StrCast(layout.dimUnit); const getMagnitude = () => String(+NumCast(layout.dimMagnitude).toFixed(3)); return ( -
+
{ diff --git a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx index 878c7ff3c..66215f109 100644 --- a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx +++ b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/require-default-props */ import { computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -20,7 +21,7 @@ export default class HeightLabel extends React.Component { const getUnit = () => StrCast(layout.dimUnit); const getMagnitude = () => String(+NumCast(layout.dimMagnitude).toFixed(decimals ?? 3)); return ( -
+
{ diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx index 73d08d5ef..ad77c327d 100644 --- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx +++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/require-default-props */ import { action } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -33,7 +34,7 @@ export default class ResizeBar extends React.Component { }; private onPointerMove = ({ movementY }: PointerEvent) => { - const { toTop: toTop, toBottom: toBottom, columnUnitLength } = this.props; + const { toTop, toBottom, columnUnitLength } = this.props; const movingDown = movementY > 0; const toNarrow = movingDown ? toBottom : toTop; const toWiden = movingDown ? toTop : toBottom; @@ -50,27 +51,6 @@ export default class ResizeBar extends React.Component { } }; - private get isActivated() { - const { toTop, toBottom } = this.props; - if (toTop && toBottom) { - if (StrCast(toTop._dimUnit, '*') === DimUnit.Pixel && StrCast(toBottom._dimUnit, '*') === DimUnit.Pixel) { - return false; - } - return true; - } else if (toTop) { - if (StrCast(toTop._dimUnit, '*') === DimUnit.Pixel) { - return false; - } - return true; - } else if (toBottom) { - if (StrCast(toBottom._dimUnit, '*') === DimUnit.Pixel) { - return false; - } - return true; - } - return false; - } - @action private onPointerUp = () => { window.removeEventListener('pointermove', this.onPointerMove); @@ -88,7 +68,7 @@ export default class ResizeBar extends React.Component { height: this.props.height, backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor), }}> -
this.registerResizing(e)} /> +
this.registerResizing(e)} />
); } diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx index 2823e1936..389fc66b3 100644 --- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx +++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx @@ -1,5 +1,6 @@ +/* eslint-disable react/no-unused-prop-types */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable } from 'mobx'; +import { action } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { setupMoveUpEvents } from '../../../../ClientUtils'; @@ -25,8 +26,6 @@ export interface SchemaColumnHeaderProps { @observer export class SchemaColumnHeader extends React.Component { - @observable _ref: HTMLDivElement | null = null; - get fieldKey() { return this.props.columnKeys[this.props.columnIndex]; } @@ -35,9 +34,9 @@ export class SchemaColumnHeader extends React.Component sortClicked = (e: React.PointerEvent) => { e.stopPropagation(); e.preventDefault(); - if (this.props.sortField == this.fieldKey && this.props.sortDesc) { + if (this.props.sortField === this.fieldKey && this.props.sortDesc) { this.props.setSort(undefined); - } else if (this.props.sortField == this.fieldKey) { + } else if (this.props.sortField === this.fieldKey) { this.props.setSort(this.fieldKey, true); } else { this.props.setSort(this.fieldKey, false); @@ -46,7 +45,7 @@ export class SchemaColumnHeader extends React.Component @action onPointerDown = (e: React.PointerEvent) => { - this.props.isContentActive(true) && setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction, false); + this.props.isContentActive(true) && setupMoveUpEvents(this, e, moveEv => this.props.dragColumn(moveEv, this.props.columnIndex), emptyFunction, emptyFunction, false); }; render() { @@ -59,19 +58,18 @@ export class SchemaColumnHeader extends React.Component onPointerDown={this.onPointerDown} ref={col => { if (col) { - this._ref = col; this.props.setColRef(this.props.columnIndex, col); } }}> -
this.props.resizeColumn(e, this.props.columnIndex)}>
+
this.props.resizeColumn(e, this.props.columnIndex)} />
{this.fieldKey}
this.props.openContextMenu(e.clientX, e.clientY, this.props.columnIndex)}>
-
- +
+
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 27a4493cb..61afe08cf 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -63,7 +63,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { } }; - onPointerEnter = (e: any) => { + onPointerEnter = () => { if (SnappingManager.IsDragging && this._props.isContentActive()) { document.removeEventListener('pointermove', this.onPointerMove); document.addEventListener('pointermove', this.onPointerMove); @@ -75,7 +75,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { if (this._ref && dragIsRow) { const rect = this._ref.getBoundingClientRect(); - const y = e.clientY - rect.top; //y position within the element. + const y = e.clientY - rect.top; // y position within the element. const height = this._ref.clientHeight; const halfLine = height / 2; if (y <= halfLine) { @@ -90,7 +90,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { } }; - onPointerLeave = (e: any) => { + onPointerLeave = () => { if (this._ref) { this._ref.style.borderTop = '0px'; this._ref.style.borderBottom = '0px'; @@ -131,15 +131,16 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { e, returnFalse, emptyFunction, - undoable(e => { - e.stopPropagation(); + undoable(clickEv => { + clickEv.stopPropagation(); Doc.toggleLockedPosition(this.Document); }, 'Delete Row') ) - }> + } + /> } + icon={} size={Size.XSMALL} onPointerDown={e => setupMoveUpEvents( @@ -147,8 +148,8 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { e, returnFalse, emptyFunction, - undoable(e => { - e.stopPropagation(); + undoable(clickEv => { + clickEv.stopPropagation(); this._props.removeDocument?.(this.Document); }, 'Delete Row') ) @@ -164,8 +165,8 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { e, returnFalse, emptyFunction, - undoable(e => { - e.stopPropagation(); + undoable(clickEv => { + clickEv.stopPropagation(); this._props.addDocTab(this.Document, OpenWhere.addRight); }, 'Open schema Doc preview') ) diff --git a/src/client/views/global/globalEnums.tsx b/src/client/views/global/globalEnums.tsx index 610c2b102..2cf9e4162 100644 --- a/src/client/views/global/globalEnums.tsx +++ b/src/client/views/global/globalEnums.tsx @@ -1,44 +1,44 @@ export enum Colors { - BLACK = "#000000", - DARK_GRAY = "#323232", - MEDIUM_GRAY = "#9F9F9F", - LIGHT_GRAY = "#DFDFDF", - WHITE = "#FFFFFF", - MEDIUM_BLUE = "#4476F7", - MEDIUM_BLUE_ALT = "#4476f73d", // REDUCED OPACITY - LIGHT_BLUE = "#BDDDF5", - PINK = "#E0217D", - ERROR_RED = "#ff0033", - YELLOW = "#F5D747", - DROP_SHADOW = "#32323215", + BLACK = '#000000', + DARK_GRAY = '#323232', + MEDIUM_GRAY = '#9F9F9F', + LIGHT_GRAY = '#DFDFDF', + WHITE = '#FFFFFF', + MEDIUM_BLUE = '#4476F7', + MEDIUM_BLUE_ALT = '#4476f73d', // REDUCED OPACITY + LIGHT_BLUE = '#BDDDF5', + PINK = '#E0217D', + ERROR_RED = '#ff0033', + YELLOW = '#F5D747', + DROP_SHADOW = '#32323215', } export enum FontSizes { - //Bolded - LARGE_HEADER = "16px", + // Bolded + LARGE_HEADER = '16px', - //Bolded or unbolded - BODY_TEXT = "12px", + // Bolded or unbolded + BODY_TEXT = '12px', - //Bolded - SMALL_TEXT = "9px", + // Bolded + SMALL_TEXT = '9px', } export enum Padding { - MINIMUM_PADDING = "4px", - SMALL_PADDING = "8px", - MEDIUM_PADDING = "16px", - LARGE_PADDING = "32px", + MINIMUM_PADDING = '4px', + SMALL_PADDING = '8px', + MEDIUM_PADDING = '16px', + LARGE_PADDING = '32px', } export enum IconSizes { - ICON_SIZE = "28px", + ICON_SIZE = '28px', } export enum Borders { - STANDARD = "solid 1px #9F9F9F" + STANDARD = 'solid 1px #9F9F9F', } export enum Shadows { - STANDARD_SHADOW = "0px 3px 4px rgba(0, 0, 0, 0.3)" -} \ No newline at end of file + STANDARD_SHADOW = '0px 3px 4px rgba(0, 0, 0, 0.3)', +} diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx index 60def5d45..f99a18db2 100644 --- a/src/client/views/linking/LinkMenuGroup.tsx +++ b/src/client/views/linking/LinkMenuGroup.tsx @@ -1,14 +1,17 @@ +/* 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'; -import { observable, action } from 'mobx'; +import * as React from 'react'; import { Doc, StrListCast } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { Cast, DocCast } from '../../../fields/Types'; +import { DocumentType } from '../../documents/DocumentTypes'; import { LinkManager } from '../../util/LinkManager'; import { DocumentView } from '../nodes/DocumentView'; import './LinkMenu.scss'; import { LinkMenuItem } from './LinkMenuItem'; -import * as React from 'react'; -import { DocumentType } from '../../documents/DocumentTypes'; interface LinkMenuGroupProps { sourceDoc: Doc; @@ -22,25 +25,24 @@ interface LinkMenuGroupProps { @observer export class LinkMenuGroup extends React.Component { private _menuRef = React.createRef(); + @observable _collapsed = false; getBackgroundColor = (): string | undefined => { - const link_relationshipList = StrListCast(Doc.UserDoc().link_relationshipList); + const linkRelationshipList = StrListCast(Doc.UserDoc().link_relationshipList); const linkColorList = StrListCast(Doc.UserDoc().link_ColorList); let color: string | undefined; // if this link's relationship property is not default "link", set its color - if (link_relationshipList) { - const relationshipIndex = link_relationshipList.indexOf(this.props.groupType); + if (linkRelationshipList) { + const relationshipIndex = linkRelationshipList.indexOf(this.props.groupType); const RGBcolor: string = linkColorList[relationshipIndex]; if (RGBcolor) { - //set opacity to 0.25 by modifiying the rgb string + // set opacity to 0.25 by modifiying the rgb string color = RGBcolor.slice(0, RGBcolor.length - 1) + ', 0.25)'; } } return color; }; - @observable _collapsed = false; - render() { const set = new Set(this.props.group); const groupItems = Array.from(set.keys()).map(linkDoc => { @@ -76,7 +78,12 @@ export class LinkMenuGroup extends React.Component { return (
-
(this._collapsed = !this._collapsed))} style={{ background: this.getBackgroundColor() }}> +
{ + this._collapsed = !this._collapsed; + })} + style={{ background: this.getBackgroundColor() }}>

{this.props.groupType}:

{this._collapsed ? null :
{groupItems}
} diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 9be78a6cb..303ff4b98 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -92,10 +92,10 @@ export class LinkMenuItem extends ObservableReactComponent { setupMoveUpEvents( this, e, - e => { + moveEv => { const dragData = new DragManager.DocumentDragData([this._props.linkDoc], dropActionType.embed); dragData.dropPropertiesToRemove = ['hidden']; - DragManager.StartDocumentDrag([this._editRef.current!], dragData, e.x, e.y); + DragManager.StartDocumentDrag([this._editRef.current!], dragData, moveEv.x, moveEv.y); return true; }, emptyFunction, @@ -125,10 +125,10 @@ export class LinkMenuItem extends ObservableReactComponent { setupMoveUpEvents( this, e, - e => { + moveEv => { const eleClone: any = this._drag.current!.cloneNode(true); - eleClone.style.transform = `translate(${e.x}px, ${e.y}px)`; - StartLinkTargetsDrag(eleClone, this._props.docView, e.x, e.y, this._props.sourceDoc, [this._props.linkDoc]); + 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]); this._props.clearLinkEditor?.(); return true; }, diff --git a/src/client/views/linking/LinkRelationshipSearch.tsx b/src/client/views/linking/LinkRelationshipSearch.tsx deleted file mode 100644 index 0902d53b2..000000000 --- a/src/client/views/linking/LinkRelationshipSearch.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { observer } from 'mobx-react'; -import * as React from 'react'; -import './LinkEditor.scss'; - -interface link_relationshipSearchProps { - results: string[] | undefined; - display: string; - //callback fn to set rel + hide dropdown upon setting - handleRelationshipSearchChange: (result: string) => void; - toggleSearch: () => void; -} -@observer -export class link_relationshipSearch extends React.Component { - handleResultClick = (e: React.MouseEvent) => { - const relationship = (e.target as HTMLParagraphElement).textContent; - if (relationship) { - this.props.handleRelationshipSearchChange(relationship); - } - }; - - handleMouseEnter = () => { - this.props.toggleSearch(); - }; - - handleMouseLeave = () => { - this.props.toggleSearch(); - }; - - /** - * Render an empty div to increase the height of LinkEditor to accommodate 2+ results - */ - emptyDiv = () => { - if (this.props.results && this.props.results.length > 2 && this.props.display === 'block') { - return
; - } - }; - - render() { - return ( -
-
- { - // return a dropdown of relationship results if there exist results - this.props.results ? ( - this.props.results.map(result => { - return ( -

- {result} -

- ); - }) - ) : ( -

No matching relationships

- ) - } -
- - {/*Render an empty div to increase the height of LinkEditor to accommodate 2+ results */} - {this.emptyDiv()} -
- ); - } -} diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index c37466914..9251dca6d 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -532,7 +532,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent() { e, returnFalse, returnFalse, - action((e: PointerEvent, doubleTap?: boolean) => { + action((moveEv: PointerEvent, doubleTap?: boolean) => { if (doubleTap) { this.startTrim(TrimScope.All); } else if (this.timeline) { @@ -572,14 +572,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent() { setupTimelineDrop = (r: HTMLDivElement | null) => { if (r && this.timeline) { this._dropDisposer?.(); - this._dropDisposer = DragManager.MakeDropTarget( - r, - (e, de) => { - const [xp] = this.ScreenToLocalBoxXf().transformPoint(de.x, de.y); - de.complete.docDragData && this.timeline?.internalDocDrop(e, de, de.complete.docDragData, xp); - }, - this.layoutDoc - ); + this._dropDisposer = DragManager.MakeDropTarget(r, (e, de) => de.complete.docDragData && this.timeline?.internalDocDrop(e, de, de.complete.docDragData), this.layoutDoc); } }; diff --git a/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx b/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx index 0084d7394..60bc8df18 100644 --- a/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx +++ b/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx @@ -1,3 +1,5 @@ +/* eslint-disable jsx-a11y/label-has-associated-control */ +/* eslint-disable jsx-a11y/alt-text */ import { IconButton } from 'browndash-components'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; @@ -15,56 +17,52 @@ interface SchemaCSVPopUpProps {} @observer export class SchemaCSVPopUp extends React.Component { + // eslint-disable-next-line no-use-before-define static Instance: SchemaCSVPopUp; - @observable - public dataVizDoc: Doc | undefined = undefined; + @observable public dataVizDoc: Doc | undefined = undefined; + @observable public view: DocumentView | undefined = undefined; + @observable public target: Doc | undefined = undefined; + @observable public visible: boolean = false; + + constructor(props: SchemaCSVPopUpProps) { + super(props); + makeObservable(this); + SchemaCSVPopUp.Instance = this; + } + @action public setDataVizDoc = (doc: Doc) => { this.dataVizDoc = doc; }; - @observable - public view: DocumentView | undefined = undefined; @action public setView = (docView: DocumentView) => { this.view = docView; }; - @observable - public target: Doc | undefined = undefined; @action public setTarget = (doc: Doc) => { this.target = doc; }; - @observable - public visible: boolean = false; @action public setVisible = (vis: boolean) => { this.visible = vis; }; - constructor(props: SchemaCSVPopUpProps) { - super(props); - makeObservable(this); - SchemaCSVPopUp.Instance = this; - } - - dataBox = () => { - return ( -
- {this.heading('Schema Table as Data Visualization Doc')} -
-
-
this.drag(e)}> - -
+ dataBox = () => ( +
+ {this.heading('Schema Table as Data Visualization Doc')} +
+
+
this.drag(e)}> +
- ); - }; +
+ ); heading = (headingText: string) => (
@@ -79,24 +77,22 @@ export class SchemaCSVPopUp extends React.Component { setupMoveUpEvents( {}, e, - e => { + moveEv => { const sourceAnchorCreator = () => this.dataVizDoc!; - const targetCreator = (annotationOn: Doc | undefined) => { + const targetCreator = () => { const embedding = Doc.MakeEmbedding(this.dataVizDoc!); return embedding; }; - if (this.view && sourceAnchorCreator && !ClientUtils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) { - DragManager.StartAnchorAnnoDrag(e.target instanceof HTMLElement ? [e.target] : [], new DragManager.AnchorAnnoDragData(this.view, sourceAnchorCreator, targetCreator), downX, downY, { - dragComplete: e => { - this.setVisible(false); - }, + if (this.view && sourceAnchorCreator && !ClientUtils.isClick(moveEv.clientX, moveEv.clientY, downX, downY, Date.now())) { + DragManager.StartAnchorAnnoDrag(moveEv.target instanceof HTMLElement ? [moveEv.target] : [], new DragManager.AnchorAnnoDragData(this.view, sourceAnchorCreator, targetCreator), downX, downY, { + dragComplete: () => this.setVisible(false), }); return true; } return false; }, emptyFunction, - action(e => {}) + action(() => {}) ); }; diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index 2735a40d5..910504e7c 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -75,8 +75,8 @@ export class PieChart extends ObservableReactComponent { } @computed get defaultGraphTitle() { - var ax0 = this._props.axes[0]; - var ax1 = this._props.axes.length > 1 ? this._props.axes[1] : undefined; + const ax0 = this._props.axes[0]; + const ax1 = this._props.axes.length > 1 ? this._props.axes[1] : undefined; if (this._props.axes.length < 2 || !/\d/.test(this._props.records[0][ax0]) || !ax1) { return ax0 + ' Pie Chart'; } @@ -101,8 +101,6 @@ export class PieChart extends ObservableReactComponent { ); } - @action - restoreView = (data: Doc) => {}; // create a document anchor that stores whatever is needed to reconstruct the viewing state (selection,zoom,etc) getAnchor = (pinProps?: PinProps) => { const anchor = Docs.Create.ConfigDocument({ @@ -130,23 +128,23 @@ export class PieChart extends ObservableReactComponent { : validData.map((d: { [x: string]: any }) => this.byCategory ? d[field] // - : +d[field].replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\ { - var index = -1; - var sameAsCurrent: boolean; + let index = -1; + let sameAsCurrent: boolean; const selected = svg.selectAll('.slice').filter((d: any) => { index++; - var p1 = [0, 0]; // center of pie - var p3 = [arc.centroid(d)[0] * 2, arc.centroid(d)[1] * 2]; // outward peak of arc - var p2 = [radius * Math.sin(d.startAngle), -radius * Math.cos(d.startAngle)]; // start of arc - var p4 = [radius * Math.sin(d.endAngle), -radius * Math.cos(d.endAngle)]; // end of arc + const p1 = [0, 0]; // center of pie + const p3 = [arc.centroid(d)[0] * 2, arc.centroid(d)[1] * 2]; // outward peak of arc + const p2 = [radius * Math.sin(d.startAngle), -radius * Math.cos(d.startAngle)]; // start of arc + const p4 = [radius * Math.sin(d.endAngle), -radius * Math.cos(d.endAngle)]; // end of arc // draw an imaginary horizontal line from the pointer to see how many times it crosses a slice edge - var lineCrossCount = 0; + let lineCrossCount = 0; // if for all 4 lines if (Math.min(p1[1], p2[1]) <= pointer[1] && pointer[1] <= Math.max(p1[1], p2[1])) { // within y bounds @@ -161,13 +159,13 @@ export class PieChart extends ObservableReactComponent { if (Math.min(p4[1], p1[1]) <= pointer[1] && pointer[1] <= Math.max(p4[1], p1[1])) { if (pointer[0] <= ((pointer[1] - p4[1]) * (p1[0] - p4[0])) / (p1[1] - p4[1]) + p4[0]) lineCrossCount++; } - if (lineCrossCount % 2 != 0) { + if (lineCrossCount % 2 !== 0) { // inside the slice of it crosses an odd number of edges - var showSelected = this.byCategory ? pieDataSet[index] : this._pieChartData[index]; + const showSelected = this.byCategory ? pieDataSet[index] : this._pieChartData[index]; if (changeSelectedVariables) { // for when a bar is selected - not just hovered over sameAsCurrent = this._currSelected - ? showSelected[Object.keys(showSelected)[0]] == this._currSelected![Object.keys(showSelected)[0]] && showSelected[Object.keys(showSelected)[1]] == this._currSelected![Object.keys(showSelected)[1]] + ? showSelected[Object.keys(showSelected)[0]] === this._currSelected![Object.keys(showSelected)[0]] && showSelected[Object.keys(showSelected)[1]] === this._currSelected![Object.keys(showSelected)[1]] : this._currSelected === showSelected; this._currSelected = sameAsCurrent ? undefined : showSelected; this.selectedData = sameAsCurrent ? undefined : d; @@ -187,104 +185,100 @@ export class PieChart extends ObservableReactComponent { d3.select(this._piechartRef.current).select('svg').remove(); d3.select(this._piechartRef.current).select('.tooltip').remove(); - var percentField = Object.keys(dataSet[0])[0]; - var descriptionField = Object.keys(dataSet[0])[1]!; - var radius = Math.min(width, height - this._props.margin.top - this._props.margin.bottom) / 2; + let percentField = Object.keys(dataSet[0])[0]; + let descriptionField = Object.keys(dataSet[0])[1]!; + const radius = Math.min(width, height - this._props.margin.top - this._props.margin.bottom) / 2; // converts data into Objects - var data = this.data(dataSet); - var pieDataSet = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || isNaN(d[key]))); + let data = this.data(dataSet); + let pieDataSet = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || isNaN(d[key]))); if (this.byCategory) { - let uniqueCategories = [...new Set(data)]; - var pieStringDataSet: { frequency: number }[] = []; + const uniqueCategories = [...new Set(data)]; + const pieStringDataSet: { frequency: number }[] = []; for (let i = 0; i < uniqueCategories.length; i++) { pieStringDataSet.push({ frequency: 0, [percentField]: uniqueCategories[i] }); } for (let i = 0; i < data.length; i++) { - let sliceData = pieStringDataSet.filter((each: any) => each[percentField] == data[i]); - sliceData[0].frequency = sliceData[0].frequency + 1; + // eslint-disable-next-line no-loop-func + const sliceData = pieStringDataSet.filter((each: any) => each[percentField] === data[i]); + sliceData[0].frequency += 1; } pieDataSet = pieStringDataSet; - percentField = Object.keys(pieDataSet[0])[0]; - descriptionField = Object.keys(pieDataSet[0])[1]!; + [percentField, descriptionField] = Object.keys(pieDataSet[0]); data = this.data(pieStringDataSet); } - var trackDuplicates: { [key: string]: any } = {}; - data.forEach((eachData: any) => (!trackDuplicates[eachData] ? (trackDuplicates[eachData] = 0) : null)); + let trackDuplicates: { [key: string]: any } = {}; + data.forEach((eachData: any) => { + !trackDuplicates[eachData] ? (trackDuplicates[eachData] = 0) : null; + }); // initial chart - var svg = (this._piechartSvg = d3 + const svg = (this._piechartSvg = d3 .select(this._piechartRef.current) .append('svg') .attr('class', 'graph') .attr('width', width + this._props.margin.right + this._props.margin.left) .attr('height', height + this._props.margin.top + this._props.margin.bottom) .append('g')); - let g = svg.append('g').attr('transform', 'translate(' + (width / 2 + this._props.margin.left) + ',' + height / 2 + ')'); - var pie = d3.pie(); - var arc = d3.arc().innerRadius(0).outerRadius(radius); + const g = svg.append('g').attr('transform', 'translate(' + (width / 2 + this._props.margin.left) + ',' + height / 2 + ')'); + const pie = d3.pie(); + const arc = d3.arc().innerRadius(0).outerRadius(radius); + const updateHighlights = () => { + const hoverOverSlice = this.hoverOverData; + const { selectedData } = this; + svg.selectAll('path').attr('class', (d: any) => + (selectedData && d.startAngle === selectedData.startAngle && d.endAngle === selectedData.endAngle) || (hoverOverSlice && d.startAngle === hoverOverSlice.startAngle && d.endAngle === hoverOverSlice.endAngle) ? 'slice hover' : 'slice' + ); + }; // click/hover const onPointClick = action((e: any) => this.highlightSelectedSlice(true, svg, arc, radius, d3.pointer(e), pieDataSet)); const onHover = action((e: any) => { this.highlightSelectedSlice(false, svg, arc, radius, d3.pointer(e), pieDataSet); updateHighlights(); }); - const mouseOut = action((e: any) => { + const mouseOut = action(() => { this.hoverOverData = undefined; updateHighlights(); }); - const updateHighlights = () => { - const hoverOverSlice = this.hoverOverData; - const selectedData = this.selectedData; - svg.selectAll('path').attr('class', function (d: any) { - return (selectedData && d.startAngle == selectedData.startAngle && d.endAngle == selectedData.endAngle) || (hoverOverSlice && d.startAngle == hoverOverSlice.startAngle && d.endAngle == hoverOverSlice.endAngle) - ? 'slice hover' - : 'slice'; - }); - }; // drawing the slices - var selected = this.selectedData; - var arcs = g.selectAll('arc').data(pie(data)).enter().append('g'); + const selected = this.selectedData; + const arcs = g.selectAll('arc').data(pie(data)).enter().append('g'); const possibleDataPointVals: { [x: string]: any }[] = []; pieDataSet.forEach((each: { [x: string]: any | { valueOf(): number } }) => { - var dataPointVal: { [x: string]: any } = {}; + const dataPointVal: { [x: string]: any } = {}; dataPointVal[percentField] = each[percentField]; if (descriptionField) dataPointVal[descriptionField] = each[descriptionField]; try { - dataPointVal[percentField] = Number(dataPointVal[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\ each.split('::')); arcs.append('path') .attr('fill', (d, i) => { - var dataPoint; + let dataPoint; const possibleDataPoints = possibleDataPointVals.filter((pval: any) => pval[percentField] === Number(d.data)); - if (possibleDataPoints.length == 1) dataPoint = possibleDataPoints[0]; + if (possibleDataPoints.length === 1) [dataPoint] = possibleDataPoints; else { dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]]; trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1; } - var sliceColor; + let sliceColor; if (dataPoint) { const sliceTitle = dataPoint[this._props.axes[0]]; - const accessByName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\ each[0] == accessByName && (sliceColor = each[1])); + const accessByName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/%/g, '').replace(/#/g, '').replace(/ { + // eslint-disable-next-line prefer-destructuring + each[0] === accessByName && (sliceColor = each[1]); + }); } return sliceColor ? StrCast(sliceColor) : d3.schemeSet3[i] ? d3.schemeSet3[i] : d3.schemeSet3[i % d3.schemeSet3.length]; }) - .attr( - 'class', - selected - ? function (d) { - return selected && d.startAngle == selected.startAngle && d.endAngle == selected.endAngle ? 'slice hover' : 'slice'; - } - : function (d) { - return 'slice'; - } - ) + .attr('class', selected ? d => (selected && d.startAngle === selected.startAngle && d.endAngle === selected.endAngle ? 'slice hover' : 'slice') : () => 'slice') // @ts-ignore .attr('d', arc) .on('click', onPointClick) @@ -293,20 +287,22 @@ export class PieChart extends ObservableReactComponent { // adding labels trackDuplicates = {}; - data.forEach((eachData: any) => (!trackDuplicates[eachData] ? (trackDuplicates[eachData] = 0) : null)); + data.forEach((eachData: any) => { + !trackDuplicates[eachData] ? (trackDuplicates[eachData] = 0) : null; + }); arcs.size() < 100 && arcs .append('text') - .attr('transform', function (d) { - var centroid = arc.centroid(d as unknown as d3.DefaultArcObject); - var heightOffset = (centroid[1] / radius) * Math.abs(centroid[1]); + .attr('transform', d => { + const centroid = arc.centroid(d as unknown as d3.DefaultArcObject); + const heightOffset = (centroid[1] / radius) * Math.abs(centroid[1]); return 'translate(' + (centroid[0] + centroid[0] / (radius * 0.02)) + ',' + (centroid[1] + heightOffset) + ')'; }) .attr('text-anchor', 'middle') - .text(function (d) { - var dataPoint; + .text(d => { + let dataPoint; const possibleDataPoints = possibleDataPointVals.filter((pval: any) => pval[percentField] === Number(d.data)); - if (possibleDataPoints.length == 1) dataPoint = pieDataSet[possibleDataPointVals.indexOf(possibleDataPoints[0])]; + if (possibleDataPoints.length === 1) dataPoint = pieDataSet[possibleDataPointVals.indexOf(possibleDataPoints[0])]; else { dataPoint = pieDataSet[possibleDataPointVals.indexOf(possibleDataPoints[trackDuplicates[d.data.toString()]])]; trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1; @@ -318,11 +314,11 @@ export class PieChart extends ObservableReactComponent { @action changeSelectedColor = (color: string) => { this.curSliceSelected.attr('fill', color); const sliceTitle = this._currSelected[this._props.axes[0]]; - const sliceName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\ { - if (each.split('::')[0] == sliceName) sliceColors.splice(sliceColors.indexOf(each), 1); + sliceColors.forEach(each => { + if (each.split('::')[0] === sliceName) sliceColors.splice(sliceColors.indexOf(each), 1); }); sliceColors.push(StrCast(sliceName + '::' + color)); }; @@ -333,38 +329,39 @@ export class PieChart extends ObservableReactComponent { }; render() { - var titleAccessor: any = 'dataViz_pie_title'; - if (this._props.axes.length == 2) titleAccessor = titleAccessor + this._props.axes[0] + '-' + this._props.axes[1]; - else if (this._props.axes.length > 0) titleAccessor = titleAccessor + this._props.axes[0]; + let titleAccessor: any = 'dataViz_pie_title'; + if (this._props.axes.length === 2) titleAccessor = titleAccessor + this._props.axes[0] + '-' + this._props.axes[1]; + else if (this._props.axes.length > 0) titleAccessor += this._props.axes[0]; if (!this._props.layoutDoc[titleAccessor]) this._props.layoutDoc[titleAccessor] = this.defaultGraphTitle; if (!this._props.layoutDoc.dataViz_pie_sliceColors) this._props.layoutDoc.dataViz_pie_sliceColors = new List(); - var selected: string; - var curSelectedSliceName = ''; + let selected: string; + let curSelectedSliceName = ''; if (this._currSelected) { selected = '{ '; const sliceTitle = this._currSelected[this._props.axes[0]]; - curSelectedSliceName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\ { - key != '' ? (selected += key + ': ' + this._currSelected[key] + ', ') : ''; + curSelectedSliceName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/%/g, '').replace(/#/g, '').replace(/ { + key !== '' ? (selected += key + ': ' + this._currSelected[key] + ', ') : ''; }); selected = selected.substring(0, selected.length - 2); selected += ' }'; - if (this._props.titleCol != '' && (!this._currSelected['frequency'] || this._currSelected['frequency'] < 10)) { + if (this._props.titleCol !== '' && (!this._currSelected.frequency || this._currSelected.frequency < 10)) { selected += '\n' + this._props.titleCol + ': '; this._tableData.forEach(each => { - if (this._currSelected[this._props.axes[0]] == each[this._props.axes[0]]) { + if (this._currSelected[this._props.axes[0]] === each[this._props.axes[0]]) { if (this._props.axes[1]) { - if (this._currSelected[this._props.axes[1]] == each[this._props.axes[1]]) selected += each[this._props.titleCol] + ', '; + if (this._currSelected[this._props.axes[1]] === each[this._props.axes[1]]) selected += each[this._props.titleCol] + ', '; } else selected += each[this._props.titleCol] + ', '; } }); selected = selected.slice(0, -1).slice(0, -1); } } else selected = 'none'; - var selectedSliceColor; - var sliceColors = StrListCast(this._props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::')); + let selectedSliceColor; + const sliceColors = StrListCast(this._props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::')); sliceColors.forEach(each => { - if (each[0] == curSelectedSliceName!) selectedSliceColor = each[1]; + // eslint-disable-next-line prefer-destructuring + if (each[0] === curSelectedSliceName!) selectedSliceColor = each[1]; }); if (this._pieChartData.length > 0 || !this.parentViz) { @@ -374,30 +371,32 @@ export class PieChart extends ObservableReactComponent { (this._props.layoutDoc[titleAccessor] = val as string)), + action(val => { + this._props.layoutDoc[titleAccessor] = val as string; + }), 'Change Graph Title' )} - color={'black'} + color="black" size={Size.LARGE} fillWidth />
{this._props.axes.length === 1 && /\d/.test(this._props.records[0][this._props.axes[0]]) ? ( -
+
Organize data as histogram
) : null}
- {selected != 'none' ? ( -
+ {selected !== 'none' ? ( +
Selected: {selected}     } - selectedColor={selectedSliceColor ? selectedSliceColor : this.curSliceSelected.attr('fill')} + selectedColor={selectedSliceColor || this.curSliceSelected.attr('fill')} setFinalColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Slice Color')} setSelectedColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Slice Color')} size={Size.XSMALL} @@ -406,12 +405,12 @@ export class PieChart extends ObservableReactComponent { ) : null}
) : ( - {'first use table view to select a column to graph'} - ); - } else - return ( - // when it is a brushed table and the incoming table doesn't have any rows selected -
Selected rows of data from the incoming DataVizBox to display.
+ first use table view to select a column to graph ); + } + return ( + // when it is a brushed table and the incoming table doesn't have any rows selected +
Selected rows of data from the incoming DataVizBox to display.
+ ); } } diff --git a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts index 336935d23..be05c3529 100644 --- a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts +++ b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts @@ -5,11 +5,11 @@ import { DataPoint } from '../components/LineChart'; export const minMaxRange = (dataPts: DataPoint[][]) => { // find the max and min of all the data points - const yMin = d3.min(dataPts, d => d3.min(d, d => Number(d.y))); - const yMax = d3.max(dataPts, d => d3.max(d, d => Number(d.y))); + const yMin = d3.min(dataPts, d => d3.min(d, m => Number(m.y))); + const yMax = d3.max(dataPts, d => d3.max(d, m => Number(m.y))); - const xMin = d3.min(dataPts, d => d3.min(d, d => Number(d.x))); - const xMax = d3.max(dataPts, d => d3.max(d, d => Number(d.x))); + const xMin = d3.min(dataPts, d => d3.min(d, m => Number(m.x))); + const xMax = d3.max(dataPts, d => d3.max(d, m => Number(m.x))); return { xMin, xMax, yMin, yMax }; }; @@ -20,18 +20,15 @@ export const scaleCreatorCategorical = (labels: string[], range: number[]) => { return scale; }; -export const scaleCreatorNumerical = (domA: number, domB: number, rangeA: number, rangeB: number) => { - return d3.scaleLinear().domain([domA, domB]).range([rangeA, rangeB]); -}; +export const scaleCreatorNumerical = (domA: number, domB: number, rangeA: number, rangeB: number) => d3.scaleLinear().domain([domA, domB]).range([rangeA, rangeB]); -export const createLineGenerator = (xScale: d3.ScaleLinear, yScale: d3.ScaleLinear) => { +export const createLineGenerator = (xScale: d3.ScaleLinear, yScale: d3.ScaleLinear) => // TODO: nda - look into the different types of curves - return d3 + d3 .line() .x(d => xScale(d.x)) .y(d => yScale(d.y)) .curve(d3.curveMonotoneX); -}; export const xAxisCreator = (g: d3.Selection, height: number, xScale: d3.ScaleLinear) => { g.attr('class', 'x-axis').attr('transform', `translate(0,${height})`).call(d3.axisBottom(xScale).tickSize(15)); @@ -48,7 +45,7 @@ export const xGrid = (g: d3.Selection, he d3 .axisBottom(scale) .tickSize(-height) - .tickFormat((a, b) => '') + .tickFormat((/* a, b */) => '') ); }; @@ -57,10 +54,16 @@ export const yGrid = (g: d3.Selection, wi d3 .axisLeft(scale) .tickSize(-width) - .tickFormat((a, b) => '') + .tickFormat((/* a, b */) => '') ); }; export const drawLine = (p: d3.Selection, dataPts: DataPoint[], lineGen: d3.Line, extra: boolean) => { - p.datum(dataPts).attr('fill', 'none').attr('stroke', 'rgba(53, 162, 235, 0.5)').attr('stroke-width', 2).attr('stroke', extra? 'blue' : 'black').attr('class', 'line').attr('d', lineGen); + p.datum(dataPts) + .attr('fill', 'none') + .attr('stroke', 'rgba(53, 162, 235, 0.5)') + .attr('stroke-width', 2) + .attr('stroke', extra ? 'blue' : 'black') + .attr('class', 'line') + .attr('d', lineGen); }; diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index a029b3761..9b4e36509 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -92,7 +92,7 @@ export class DocumentLinksButton extends ObservableReactComponent { + action((clickEv, doubleTap) => { doubleTap && DocumentView.showBackLinks(this._props.View.Document); }), undefined, @@ -109,7 +109,7 @@ export class DocumentLinksButton extends ObservableReactComponent { + action((clickEv, doubleTap) => { if (doubleTap && this._props.InMenu && this._props.StartLink) { // action(() => Doc.BrushDoc(this._props.View.Document)); if (DocumentLinksButton.StartLink === this._props.View.Document) { @@ -146,7 +146,7 @@ export class DocumentLinksButton extends ObservableReactComponent DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this._props.View.Document, true, this._props.View)) + action(clickEv => DocumentLinksButton.finishLinkClick(clickEv.clientX, clickEv.clientY, DocumentLinksButton.StartLink, this._props.View.Document, true, this._props.View)) ); }; diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx index 9be66ba4a..9c216cba4 100644 --- a/src/client/views/nodes/EquationBox.tsx +++ b/src/client/views/nodes/EquationBox.tsx @@ -1,3 +1,4 @@ +/* eslint-disable jsx-a11y/no-static-element-interactions */ import { action, makeObservable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -42,7 +43,7 @@ export class EquationBox extends ViewBoxBaseComponent() { this._ref.current!.mathField.latex(text); } } - //{ fireImmediately: true } + // { fireImmediately: true } ); reaction( () => this._props.isSelected(), @@ -88,7 +89,9 @@ export class EquationBox extends ViewBoxBaseComponent() { if (e.key === 'Backspace' && !this.dataDoc.text) this._props.removeDocument?.(this.Document); }; @undoBatch - onChange = (str: string) => (this.dataDoc.text = str); + onChange = (str: string) => { + this.dataDoc.text = str; + }; updateSize = () => { const style = this._ref.current && getComputedStyle(this._ref.current.element.current); @@ -111,7 +114,7 @@ export class EquationBox extends ViewBoxBaseComponent() { const scale = (this._props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1); return (
this.updateSize()} + ref={() => this.updateSize()} className="equationBox-cont" onPointerDown={e => !e.ctrlKey && e.stopPropagation()} style={{ @@ -122,7 +125,7 @@ export class EquationBox extends ViewBoxBaseComponent() { fontSize: StrCast(this.layoutDoc._text_fontSize), }} onKeyDown={e => e.stopPropagation()}> - +
); } diff --git a/src/client/views/nodes/FaceRectangle.tsx b/src/client/views/nodes/FaceRectangle.tsx index 46bc6eb03..2b66b83fe 100644 --- a/src/client/views/nodes/FaceRectangle.tsx +++ b/src/client/views/nodes/FaceRectangle.tsx @@ -8,11 +8,17 @@ export default class FaceRectangle extends React.Component<{ rectangle: Rectangl @observable private opacity = 0; componentDidMount() { - setTimeout(() => runInAction(() => (this.opacity = 1)), 500); + setTimeout( + () => + runInAction(() => { + this.opacity = 1; + }), + 500 + ); } render() { - const rectangle = this.props.rectangle; + const { rectangle } = this.props; return (
( - - - + + - - - - - - - - - - - - - -); + /> + + + ); +} export default TrailsIcon; diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index bff6d53da..d43241de0 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -45,7 +45,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent() { onPointerDown = (e: React.PointerEvent) => { const { linkSource } = this; linkSource && - setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, (e, doubleTap) => { + setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, (clickEv, doubleTap) => { if (doubleTap) LinkFollower.FollowLink(this.Document, linkSource, false); else this._props.select(false); }); diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 2593491cc..f01905ee1 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -50,15 +51,19 @@ export class LinkBox extends ViewBoxBaseComponent() { this._props.setContentViewBox?.(this); this._disposers.deleting = reaction( () => !this.anchor1 && !this.anchor2 && this.DocumentView?.() && (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView!())), - empty => empty && ((this._hackToSeeIfDeleted = setTimeout(() => - (!this.anchor1 && !this.anchor2) && this._props.removeDocument?.(this.Document) - )), 1000) // prettier-ignore + empty => { + if (empty) { + this._hackToSeeIfDeleted = setTimeout(() => { + !this.anchor1 && !this.anchor2 && this._props.removeDocument?.(this.Document); + }, 1000); + } + } ); this._disposers.dragging = reaction( () => SnappingManager.IsDragging, () => setTimeout( action(() => {// need to wait for drag manager to set 'hidden' flag on dragged DOM elements - const a = this.anchor1, - b = this.anchor2; + const a = this.anchor1; + const b = this.anchor2; let a1 = a && document.getElementById(a.ViewGuid); let a2 = b && document.getElementById(b.ViewGuid); // test whether the anchors themselves are hidden,... @@ -67,7 +72,7 @@ export class LinkBox extends ViewBoxBaseComponent() { // .. or whether any of their DOM parents are hidden for (; a1 && !a1.hidden; a1 = a1.parentElement); for (; a2 && !a2.hidden; a2 = a2.parentElement); - this._hide = a1 || a2 ? true : false; + this._hide = !!(a1 || a2); } })) // prettier-ignore ); @@ -92,24 +97,25 @@ export class LinkBox extends ViewBoxBaseComponent() { a.Document[DocCss]; b.Document[DocCss]; + // eslint-disable-next-line @typescript-eslint/no-unused-vars const axf = a.screenToViewTransform(); // these force re-render when a or b moves (so do NOT remove) const bxf = b.screenToViewTransform(); const scale = docView?.screenToViewTransform().Scale ?? 1; const at = a.getBounds?.transition; // these force re-render when a or b change size and at the end of an animated transition const bt = b.getBounds?.transition; // inquring getBounds() also causes text anchors to update whether or not they reflow (any size change triggers an invalidation) - var foundParent = false; + let foundParent = false; const getAnchor = (field: FieldResult): Element[] => { const docField = DocCast(field); const doc = docField?.layout_unrendered ? DocCast(docField.annotationOn, docField) : docField; const ele = document.getElementById(DocumentView.UniquifyId(LightboxView.Contains(this.DocumentView?.()), doc[Id])); if (ele?.className === 'linkBox-label') foundParent = true; if (ele?.getBoundingClientRect().width) return [ele]; - const eles = Array.from(document.getElementsByClassName(doc[Id])).filter(ele => ele?.getBoundingClientRect().width); + const eles = Array.from(document.getElementsByClassName(doc[Id])).filter(el => el?.getBoundingClientRect().width); const annoOn = DocCast(doc.annotationOn); if (eles.length || !annoOn) return eles; const pareles = getAnchor(annoOn); - foundParent = pareles.length ? true : false; + foundParent = !!pareles.length; return pareles; }; // if there's an element in the DOM with a classname containing a link anchor's id (eg a hypertext ), @@ -122,26 +128,38 @@ export class LinkBox extends ViewBoxBaseComponent() { const aid = targetAhyperlinks?.find(alink => container?.contains(alink))?.id ?? targetAhyperlinks?.lastElement()?.id; const bid = targetBhyperlinks?.find(blink => container?.contains(blink))?.id ?? targetBhyperlinks?.lastElement()?.id; if (!aid || !bid) { - setTimeout(action(() => (this._forceAnimate = this._forceAnimate + 0.01))); + setTimeout( + action(() => { + this._forceAnimate += 0.01; + }) + ); return null; } if (foundParent) { setTimeout( - action(() => (this._forceAnimate = this._forceAnimate + 0.01)), + action(() => { + this._forceAnimate += 0.01; + }), 1 ); } - if (at || bt) setTimeout(action(() => (this._forceAnimate = this._forceAnimate + 0.01))); // this forces an update during a transition animation + if (at || bt) + setTimeout( + action(() => { + this._forceAnimate += 0.01; + }) + ); // this forces an update during a transition animation const highlight = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Highlighting); const highlightColor = highlight?.highlightIndex ? highlight?.highlightColor : undefined; const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color); const fontFamily = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontFamily); const fontSize = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontSize); const fontColor = (c => (c !== 'transparent' ? c : undefined))(StrCast(this.layoutDoc.link_fontColor)); - const { stroke_markerScale, stroke_width, stroke_startMarker, stroke_endMarker, stroke_dash } = this.Document; + // eslint-disable-next-line camelcase + const { stroke_markerScale: strokeMarkerScale, stroke_width: strokeRawWidth, stroke_startMarker: strokeStartMarker, stroke_endMarker: strokeEndMarker, stroke_dash: strokeDash } = this.Document; - const strokeWidth = NumCast(stroke_width, 4); + const strokeWidth = NumCast(strokeRawWidth, 4); const linkDesc = StrCast(this.dataDoc.link_description) || ' '; const labelText = linkDesc.substring(0, 50) + (linkDesc.length > 50 ? '...' : ''); return ( @@ -152,12 +170,12 @@ export class LinkBox extends ViewBoxBaseComponent() { start={aid} end={bid} // strokeWidth={strokeWidth + Math.max(2, strokeWidth * 0.1)} - showHead={stroke_startMarker ? true : false} - showTail={stroke_endMarker ? true : false} - headSize={NumCast(stroke_markerScale, 3)} - tailSize={NumCast(stroke_markerScale, 3)} - tailShape={stroke_endMarker === 'dot' ? 'circle' : 'arrow1'} - headShape={stroke_startMarker === 'dot' ? 'circle' : 'arrow1'} + showHead={!!strokeStartMarker} + showTail={!!strokeEndMarker} + headSize={NumCast(strokeMarkerScale, 3)} + tailSize={NumCast(strokeMarkerScale, 3)} + tailShape={strokeEndMarker === 'dot' ? 'circle' : 'arrow1'} + headShape={strokeStartMarker === 'dot' ? 'circle' : 'arrow1'} color={highlightColor} /> )} @@ -166,23 +184,23 @@ export class LinkBox extends ViewBoxBaseComponent() { start={aid} end={bid} // strokeWidth={strokeWidth} - dashness={Number(stroke_dash) ? true : false} - showHead={stroke_startMarker ? true : false} - showTail={stroke_endMarker ? true : false} - headSize={NumCast(stroke_markerScale, 3)} - tailSize={NumCast(stroke_markerScale, 3)} - tailShape={stroke_endMarker === 'dot' ? 'circle' : 'arrow1'} - headShape={stroke_startMarker === 'dot' ? 'circle' : 'arrow1'} + dashness={!!Number(strokeDash)} + showHead={!!strokeStartMarker} + showTail={!!strokeEndMarker} + headSize={NumCast(strokeMarkerScale, 3)} + tailSize={NumCast(strokeMarkerScale, 3)} + tailShape={strokeEndMarker === 'dot' ? 'circle' : 'arrow1'} + headShape={strokeStartMarker === 'dot' ? 'circle' : 'arrow1'} color={color} labels={
() { } setTimeout( - action(() => (this._forceAnimate = this._forceAnimate + 1)), + action(() => { + this._forceAnimate += 1; + }), 2 ); return (
{ + // eslint-disable-next-line no-use-before-define public static Instance: LinkDescriptionPopup; @observable public display: boolean = false; @observable public showDescriptions: string = 'ON'; @@ -23,6 +24,20 @@ export class LinkDescriptionPopup extends React.Component<{}> { LinkDescriptionPopup.Instance = this; } + componentDidMount() { + document.addEventListener('pointerdown', this.onClick, true); + reaction( + () => this.display, + display => { + display && (this.description = StrCast(LinkManager.Instance.currentLink?.link_description)); + } + ); + } + + componentWillUnmount() { + document.removeEventListener('pointerdown', this.onClick, true); + } + @action descriptionChanged = (e: React.ChangeEvent) => { this.description = e.currentTarget.value; @@ -39,25 +54,13 @@ export class LinkDescriptionPopup extends React.Component<{}> { @action onClick = (e: PointerEvent) => { - if (this.popupRef && !!!this.popupRef.current?.contains(e.target as any)) { + if (this.popupRef && !this.popupRef.current?.contains(e.target as any)) { this.display = false; this.description = ''; TaskCompletionBox.taskCompleted = false; } }; - componentDidMount() { - document.addEventListener('pointerdown', this.onClick, true); - reaction( - () => this.display, - display => display && (this.description = StrCast(LinkManager.Instance.currentLink?.link_description)) - ); - } - - componentWillUnmount() { - document.removeEventListener('pointerdown', this.onClick, true); - } - render() { return !this.display ? null : (
{ onChange={e => this.descriptionChanged(e)} />
-
this.onDismiss(false)}> +
this.onDismiss(false)}> {' '} Dismiss{' '}
-
this.onDismiss(true)}> +
this.onDismiss(true)}> {' '} Add{' '}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 539daf0bd..a9cfe6c0e 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -23,35 +23,41 @@ import { DocumentView, OpenWhere } from './DocumentView'; import { StyleProviderFuncType } from './FieldView'; import './LinkDocPreview.scss'; +interface LinkDocPreviewProps { + linkDoc?: Doc; + linkSrc?: Doc; + DocumentView?: () => DocumentView; + styleProvider?: StyleProviderFuncType; + location: number[]; + hrefs?: string[]; + showHeader?: boolean; + noPreview?: boolean; +} export class LinkInfo { + // eslint-disable-next-line no-use-before-define private static _instance: Opt; constructor() { LinkInfo._instance = this; makeObservable(this); } + // eslint-disable-next-line no-use-before-define @observable public LinkInfo: Opt = undefined; public static get Instance() { return LinkInfo._instance ?? new LinkInfo(); } public static Clear() { - runInAction(() => LinkInfo.Instance && (LinkInfo.Instance.LinkInfo = undefined)); + runInAction(() => { + LinkInfo.Instance && (LinkInfo.Instance.LinkInfo = undefined); + }); } public static SetLinkInfo(info?: LinkDocPreviewProps) { - runInAction(() => LinkInfo.Instance && (LinkInfo.Instance.LinkInfo = info)); + runInAction(() => { + LinkInfo.Instance && (LinkInfo.Instance.LinkInfo = info); + }); } } -interface LinkDocPreviewProps { - linkDoc?: Doc; - linkSrc?: Doc; - DocumentView?: () => DocumentView; - styleProvider?: StyleProviderFuncType; - location: number[]; - hrefs?: string[]; - showHeader?: boolean; - noPreview?: boolean; -} @observer export class LinkDocPreview extends ObservableReactComponent { _infoRef = React.createRef(); @@ -69,13 +75,13 @@ export class LinkDocPreview extends ObservableReactComponent page.summary().then(action(summary => (this._toolTipText = summary.substring(0, 500))))); + .then(page => + page.summary().then( + action(summary => { + this._toolTipText = summary.substring(0, 500); + }) + ) + ); } else { this._toolTipText = 'url => ' + href; } @@ -133,7 +145,7 @@ export class LinkDocPreview extends ObservableReactComponent { - if (Math.abs(e.clientX - down[0]) + Math.abs(e.clientY - down[1]) > 100) { - DragManager.StartDocumentDrag([this._infoRef.current!], new DragManager.DocumentDragData([this._targetDoc!]), e.pageX, e.pageY); + (moveEv, down) => { + if (Math.abs(moveEv.clientX - down[0]) + Math.abs(moveEv.clientY - down[1]) > 100) { + DragManager.StartDocumentDrag([this._infoRef.current!], new DragManager.DocumentDragData([this._targetDoc!]), moveEv.pageX, moveEv.pageY); LinkInfo.Clear(); return true; } @@ -284,18 +296,18 @@ export class LinkDocPreview extends ObservableReactComponent Doc.NativeWidth(this._targetDoc) : undefined} NativeHeight={Doc.NativeHeight(this._targetDoc) ? () => Doc.NativeHeight(this._targetDoc) : undefined} /> diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index adccc9db6..501831bca 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -60,7 +60,9 @@ export class LoadingBox extends ViewBoxAnnotatableComponent() { } else { const updateFunc = async () => { const result = await Networking.QueryYoutubeProgress(StrCast(this.Document[Id])); // We use the guid of the overwriteDoc to track file uploads. - runInAction(() => (this.progress = result.progress)); + runInAction(() => { + this.progress = result.progress; + }); !this.Document.loadingError && this._timer && (this._timer = setTimeout(updateFunc, 1000)); }; this._timer = setTimeout(updateFunc, 1000); diff --git a/src/client/views/nodes/MapBox/AnimationSpeedIcons.tsx b/src/client/views/nodes/MapBox/AnimationSpeedIcons.tsx index d54a175b2..f4ece627f 100644 --- a/src/client/views/nodes/MapBox/AnimationSpeedIcons.tsx +++ b/src/client/views/nodes/MapBox/AnimationSpeedIcons.tsx @@ -1,35 +1,44 @@ -import * as React from "react"; +import * as React from 'react'; export const slowSpeedIcon: JSX.Element = ( - - - + + ); export const mediumSpeedIcon: JSX.Element = ( - - - - - + + + + + + + ); export const fastSpeedIcon: JSX.Element = ( - - - + + + + + ); - diff --git a/src/client/views/nodes/MapBox/AnimationUtility.ts b/src/client/views/nodes/MapBox/AnimationUtility.ts index 35153f439..cf5315da3 100644 --- a/src/client/views/nodes/MapBox/AnimationUtility.ts +++ b/src/client/views/nodes/MapBox/AnimationUtility.ts @@ -87,25 +87,24 @@ export class AnimationUtility { @computed get currentPitch(): number { if (!this.isStreetViewAnimation) return 50; if (!this.terrainDisplayed) return 80; - else { - // const groundElevation = 0; - const heightAboveGround = this.currentAnimationAltitude; - const horizontalDistance = 500; - - let pitch; - if (heightAboveGround >= 0) { - pitch = 90 - Math.atan(heightAboveGround / horizontalDistance) * (180 / Math.PI); - } else { - pitch = 80; - } - console.log(Math.max(50, Math.min(pitch, 85))); + // const groundElevation = 0; + const heightAboveGround = this.currentAnimationAltitude; + const horizontalDistance = 500; - if (this.previousPitch) { - return this.lerp(Math.max(50, Math.min(pitch, 85)), this.previousPitch, 0.02); - } - return Math.max(50, Math.min(pitch, 85)); + let pitch; + if (heightAboveGround >= 0) { + pitch = 90 - Math.atan(heightAboveGround / horizontalDistance) * (180 / Math.PI); + } else { + pitch = 80; + } + + console.log(Math.max(50, Math.min(pitch, 85))); + + if (this.previousPitch) { + return this.lerp(Math.max(50, Math.min(pitch, 85)), this.previousPitch, 0.02); } + return Math.max(50, Math.min(pitch, 85)); } @computed get flyInEndPitch() { @@ -214,8 +213,8 @@ export class AnimationUtility { currentAnimationPhase: number; updateAnimationPhase: (newAnimationPhase: number) => void; updateFrameId: (newFrameId: number) => void; - }) => { - return new Promise(async resolve => { + }) => + new Promise(async resolve => { let startTime: number | null = null; const frame = async (currentTime: number) => { @@ -257,7 +256,7 @@ export class AnimationUtility { updateAnimationPhase(animationPhase); // compute corrected camera ground position, so that he leading edge of the path is in view - var correctedPosition = this.computeCameraPosition( + const correctedPosition = this.computeCameraPosition( this.isStreetViewAnimation, this.currentPitch, bearing, @@ -277,7 +276,7 @@ export class AnimationUtility { map.setFreeCameraOptions(camera); this.previousAltitude = this.currentAnimationAltitude; - this.previousPitch = this.previousPitch; + // this.previousPitch = this.previousPitch; // repeat! const innerFrameId = await window.requestAnimationFrame(frame); @@ -287,15 +286,14 @@ export class AnimationUtility { const outerFrameId = await window.requestAnimationFrame(frame); updateFrameId(outerFrameId); }); - }; - public flyInAndRotate = async ({ map, updateFrameId }: { map: MapRef; updateFrameId: (newFrameId: number) => void }) => { - return new Promise<{ bearing: number; altitude: number }>(async resolve => { + public flyInAndRotate = async ({ map, updateFrameId }: { map: MapRef; updateFrameId: (newFrameId: number) => void }) => + new Promise<{ bearing: number; altitude: number }>(async resolve => { let start: number | null; - var currentAltitude; - var currentBearing; - var currentPitch; + let currentAltitude; + let currentBearing; + let currentPitch; // the animation frame will run as many times as necessary until the duration has been reached const frame = async (time: number) => { @@ -319,7 +317,7 @@ export class AnimationUtility { currentPitch = this.FLY_IN_START_PITCH + (this.flyInEndPitch - this.FLY_IN_START_PITCH) * d3.easeCubicOut(animationPhase); // compute corrected camera ground position, so the start of the path is always in view - var correctedPosition = this.computeCameraPosition(false, currentPitch, currentBearing, this.FIRST_LNG_LAT, currentAltitude); + const correctedPosition = this.computeCameraPosition(false, currentPitch, currentBearing, this.FIRST_LNG_LAT, currentAltitude); // set the pitch and bearing of the camera const camera = map.getFreeCameraOptions(); @@ -349,13 +347,10 @@ export class AnimationUtility { const outerFrameId = await window.requestAnimationFrame(frame); updateFrameId(outerFrameId); }); - }; previousCameraPosition: { lng: number; lat: number } | null = null; - lerp = (start: number, end: number, amt: number) => { - return (1 - amt) * start + amt * end; - }; + lerp = (start: number, end: number, amt: number) => (1 - amt) * start + amt * end; computeCameraPosition = (isStreetViewAnimation: boolean, pitch: number, bearing: number, targetPosition: { lng: number; lat: number }, altitude: number, smooth = false) => { const bearingInRadian = (bearing * Math.PI) / 180; diff --git a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx index fe7f8d8b3..f176317d2 100644 --- a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx +++ b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx @@ -14,6 +14,7 @@ import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; @observer export class DirectionsAnchorMenu extends AntimodeMenu { + // eslint-disable-next-line no-use-before-define static Instance: DirectionsAnchorMenu; private _disposer: IReactionDisposer | undefined; @@ -24,8 +25,8 @@ export class DirectionsAnchorMenu extends AntimodeMenu { public OnClick: (e: PointerEvent) => void = unimplementedFunction; // public OnAudio: (e: PointerEvent) => void = unimplementedFunction; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; - public Highlight: (color: string, isTargetToggler: boolean, savedAnnotations?: ObservableMap, addAsAnnotation?: boolean) => Opt = (color: string, isTargetToggler: boolean) => undefined; - public GetAnchor: (savedAnnotations: Opt>, addAsAnnotation: boolean) => Opt = (savedAnnotations: Opt>, addAsAnnotation: boolean) => undefined; + public Highlight: (color: string, isTargetToggler: boolean, savedAnnotations?: ObservableMap, addAsAnnotation?: boolean) => Opt = () => undefined; + public GetAnchor: (savedAnnotations: Opt>, addAsAnnotation: boolean) => Opt = () => undefined; public Delete: () => void = unimplementedFunction; // public MakeTargetToggle: () => void = unimplementedFunction; // public ShowTargetTrail: () => void = unimplementedFunction; @@ -56,7 +57,7 @@ export class DirectionsAnchorMenu extends AntimodeMenu { componentDidMount() { this._disposer = reaction( () => SelectionManager.Views.slice(), - sel => DirectionsAnchorMenu.Instance.fadeOut(true) + () => DirectionsAnchorMenu.Instance.fadeOut(true) ); } // audioDown = (e: React.PointerEvent) => { @@ -104,8 +105,8 @@ export class DirectionsAnchorMenu extends AntimodeMenu { color={SettingsManager.userColor} /> - } color={SettingsManager.userColor} /> - } color={SettingsManager.userColor} /> + } color={SettingsManager.userColor} /> + } color={SettingsManager.userColor} />
); diff --git a/src/client/views/nodes/MapBox/GeocoderControl.tsx b/src/client/views/nodes/MapBox/GeocoderControl.tsx index e4ba51316..e118c57d9 100644 --- a/src/client/views/nodes/MapBox/GeocoderControl.tsx +++ b/src/client/views/nodes/MapBox/GeocoderControl.tsx @@ -3,8 +3,6 @@ // import { ControlPosition, MarkerProps, useControl } from "react-map-gl"; // import '@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css' - - // export type GeocoderControlProps = Omit & { // mapboxAccessToken: string; // marker?: Omit; @@ -31,7 +29,6 @@ // ctrl.on('results', props.onResults); // ctrl.on('result', evt => { // props.onResult(evt); - // // const {result} = evt; // // const location = // // result && @@ -49,8 +46,6 @@ // position: props.position // } // ); - - // // @ts-ignore (TS2339) private member // if (geocoder._map) { // if (geocoder.getProximity() !== props.proximity && props.proximity !== undefined) { @@ -104,4 +99,4 @@ // onLoading: noop, // onResults: noop, // onError: noop -// }; \ No newline at end of file +// }; diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx index d17c4298c..174511e1a 100644 --- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx +++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/button-has-type */ import { IconLookup, faAdd, faArrowDown, faArrowLeft, faArrowsRotate, faBicycle, faCalendarDays, faCar, faDiamondTurnRight, faEdit, faPersonWalking, faRoute } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Autocomplete, Checkbox, FormControlLabel, TextField } from '@mui/material'; @@ -24,6 +25,7 @@ type MapAnchorMenuType = 'standard' | 'routeCreation' | 'calendar' | 'customize' @observer export class MapAnchorMenu extends AntimodeMenu { + // eslint-disable-next-line no-use-before-define static Instance: MapAnchorMenu; private _disposer: IReactionDisposer | undefined; @@ -36,8 +38,8 @@ export class MapAnchorMenu extends AntimodeMenu { public OnClick: (e: PointerEvent) => void = unimplementedFunction; // public OnAudio: (e: PointerEvent) => void = unimplementedFunction; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; - public Highlight: (color: string, isTargetToggler: boolean, savedAnnotations?: ObservableMap, addAsAnnotation?: boolean) => Opt = (color: string, isTargetToggler: boolean) => undefined; - public GetAnchor: (savedAnnotations: Opt>, addAsAnnotation: boolean) => Opt = (savedAnnotations: Opt>, addAsAnnotation: boolean) => undefined; + public Highlight: (color: string, isTargetToggler: boolean, savedAnnotations?: ObservableMap, addAsAnnotation?: boolean) => Opt = () => undefined; + public GetAnchor: (savedAnnotations: Opt>, addAsAnnotation: boolean) => Opt = () => undefined; public Delete: () => void = unimplementedFunction; // public MakeTargetToggle: () => void = unimplementedFunction; // public ShowTargetTrail: () => void = unimplementedFunction; @@ -125,7 +127,7 @@ export class MapAnchorMenu extends AntimodeMenu { componentDidMount() { this._disposer = reaction( () => SelectionManager.Views.slice(), - sel => MapAnchorMenu.Instance.fadeOut(true) + () => MapAnchorMenu.Instance.fadeOut(true) ); } // audioDown = (e: React.PointerEvent) => { @@ -148,12 +150,12 @@ export class MapAnchorMenu extends AntimodeMenu { setupMoveUpEvents( this, e, - (e: PointerEvent) => { - this.StartDrag(e, this._commentRef.current!); + moveEv => { + this.StartDrag(moveEv, this._commentRef.current!); return true; }, returnFalse, - e => this.OnClick(e) + clickEv => this.OnClick(clickEv) ); }; @@ -275,7 +277,7 @@ export class MapAnchorMenu extends AntimodeMenu { HandleAddRouteClick = () => { if (this.currentRouteInfoMap && this.selectedTransportationType && this.selectedDestinationFeature) { - const coordinates = this.currentRouteInfoMap[this.selectedTransportationType].coordinates; + const { coordinates } = this.currentRouteInfoMap[this.selectedTransportationType]; console.log(coordinates); console.log(this.selectedDestinationFeature); this.AddNewRouteToMap(coordinates, this.title ?? '', this.selectedDestinationFeature, this.createPinForDestination); @@ -294,34 +296,30 @@ export class MapAnchorMenu extends AntimodeMenu { getDirectionsButton: JSX.Element = (} color={SettingsManager.userColor} />); - getAddToCalendarButton = (docType: string): JSX.Element => { - return ( - { - CalendarManager.Instance.open(undefined, docType === 'pin' ? this.pinDoc : this.routeDoc); - }} - icon={} - color={SettingsManager.userColor} - /> - ); - }; + getAddToCalendarButton = (docType: string): JSX.Element => ( + { + CalendarManager.Instance.open(undefined, docType === 'pin' ? this.pinDoc : this.routeDoc); + }} + icon={} + color={SettingsManager.userColor} + /> + ); addToCalendarButton: JSX.Element = ( CalendarManager.Instance.open(undefined, this.pinDoc)} icon={} color={SettingsManager.userColor} /> ); - getLinkNoteToDocButton = (docType: string): JSX.Element => { - return ( -
- } - color={SettingsManager.userColor} - /> -
- ); - }; + getLinkNoteToDocButton = (docType: string): JSX.Element => ( +
+ } + color={SettingsManager.userColor} + /> +
+ ); linkNoteToPinOrRoutenButton: JSX.Element = (
@@ -363,16 +361,14 @@ export class MapAnchorMenu extends AntimodeMenu { /> ); - getDeleteButton = (type: string) => { - return ( - } - color={SettingsManager.userColor} - /> - ); - }; + getDeleteButton = (type: string) => ( + } + color={SettingsManager.userColor} + /> + ); animateRouteButton: JSX.Element = ( this.OpenAnimationPanel(this.routeDoc)} icon={} color={SettingsManager.userColor} />); @@ -453,18 +449,17 @@ export class MapAnchorMenu extends AntimodeMenu { }} options={this.destinationFeatures.filter(feature => feature.place_name).map(feature => feature)} getOptionLabel={(feature: any) => feature.place_name} + // eslint-disable-next-line react/jsx-props-no-spreading renderInput={(params: any) => } /> - {this.selectedDestinationFeature && ( - <> - {!this.allMapPinDocs.some(pinDoc => pinDoc.title === this.selectedDestinationFeature.place_name) && ( -
- } /> -
- )} - - )} - @@ -517,7 +512,7 @@ export class MapAnchorMenu extends AntimodeMenu {
))}
-
+
)} {this.menuType === 'route' && this.routeDoc &&
{StrCast(this.routeDoc.title)}
} diff --git a/src/client/views/nodes/MapBox/MapboxApiUtility.ts b/src/client/views/nodes/MapBox/MapboxApiUtility.ts index 592330ac2..5c5192372 100644 --- a/src/client/views/nodes/MapBox/MapboxApiUtility.ts +++ b/src/client/views/nodes/MapBox/MapboxApiUtility.ts @@ -1,4 +1,3 @@ - const MAPBOX_FORWARD_GEOCODE_BASE_URL = 'https://api.mapbox.com/geocoding/v5/mapbox.places/'; const MAPBOX_REVERSE_GEOCODE_BASE_URL = 'https://api.mapbox.com/geocoding/v5/mapbox.places/'; const MAPBOX_DIRECTIONS_BASE_URL = 'https://api.mapbox.com/directions/v5/mapbox'; @@ -7,92 +6,79 @@ const MAPBOX_ACCESS_TOKEN = 'pk.eyJ1IjoiemF1bHRhdmFuZ2FyIiwiYSI6ImNscHgwNDd1MDA3 export type TransportationType = 'driving' | 'cycling' | 'walking'; export class MapboxApiUtility { - static forwardGeocodeForFeatures = async (searchText: string) => { try { - const url = MAPBOX_FORWARD_GEOCODE_BASE_URL + encodeURI(searchText) +'.json' +`?access_token=${MAPBOX_ACCESS_TOKEN}`; + const url = MAPBOX_FORWARD_GEOCODE_BASE_URL + encodeURI(searchText) + `.json?access_token=${MAPBOX_ACCESS_TOKEN}`; const response = await fetch(url); const data = await response.json(); return data.features; - } catch (error: any){ - // TODO: handle error in better way + } catch (error: any) { + // TODO: handle error in better way return null; } - } + }; static reverseGeocodeForFeatures = async (longitude: number, latitude: number) => { try { - const url = MAPBOX_REVERSE_GEOCODE_BASE_URL + encodeURI(longitude.toString() + "," + latitude.toString()) + '.json' + - `?access_token=${MAPBOX_ACCESS_TOKEN}`; + const url = MAPBOX_REVERSE_GEOCODE_BASE_URL + encodeURI(longitude.toString() + ',' + latitude.toString()) + `.json?access_token=${MAPBOX_ACCESS_TOKEN}`; const response = await fetch(url); const data = await response.json(); return data.features; - } catch (error: any){ + } catch (error: any) { return null; } - } + }; static getDirections = async (origin: number[], destination: number[]): Promise | undefined> => { try { - const directionsPromises: Promise[] = []; const transportationTypes: TransportationType[] = ['driving', 'cycling', 'walking']; - transportationTypes.forEach((type) => { - directionsPromises.push( - fetch( - `${MAPBOX_DIRECTIONS_BASE_URL}/${type}/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}` - ).then((response) => response.json()) - ); - }); + transportationTypes.forEach(type => { + directionsPromises.push(fetch(`${MAPBOX_DIRECTIONS_BASE_URL}/${type}/${origin[0]},${origin[1]};${destination[0]},${destination[1]}?steps=true&geometries=geojson&access_token=${MAPBOX_ACCESS_TOKEN}`).then(response => response.json())); + }); const results = await Promise.all(directionsPromises); const routeInfoMap: Record = { - 'driving': {}, - 'cycling': {}, - 'walking': {}, + driving: {}, + cycling: {}, + walking: {}, }; transportationTypes.forEach((type, index) => { const routeData = results[index].routes[0]; if (routeData) { - const geometry = routeData.geometry; - const coordinates = geometry.coordinates; - - routeInfoMap[type] = { - duration: this.secondsToMinutesHours(routeData.duration), - distance: this.metersToMiles(routeData.distance), - coordinates: coordinates, - }; + const { geometry } = routeData; + const { coordinates } = geometry; + + routeInfoMap[type] = { + duration: this.secondsToMinutesHours(routeData.duration), + distance: this.metersToMiles(routeData.distance), + coordinates: coordinates, + }; } - }); + }); return routeInfoMap; - // return current route info, and the temporary route - - } catch (error: any){ + // return current route info, and the temporary route + } catch (error: any) { return undefined; - console.log("Error: ", error); } - } + }; private static secondsToMinutesHours = (seconds: number) => { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60).toFixed(2); - if (hours === 0){ - return `${minutes} min` - } else { - return `${hours} hr ${minutes} min` + if (hours === 0) { + return `${minutes} min`; } - } - - private static metersToMiles = (meters: number) => { - return `${parseFloat((meters/1609.34).toFixed(2))} mi`; - } + return `${hours} hr ${minutes} min`; + }; + private static metersToMiles = (meters: number) => `${parseFloat((meters / 1609.34).toFixed(2))} mi`; } // const drivingQuery = await fetch( @@ -136,4 +122,4 @@ export class MapboxApiUtility { // distance: this.metersToMiles(routeData.distance), // coordinates: coordinates // } -// }) \ No newline at end of file +// }) diff --git a/src/client/views/nodes/MapBox/MarkerIcons.tsx b/src/client/views/nodes/MapBox/MarkerIcons.tsx index a580fcaa0..087472112 100644 --- a/src/client/views/nodes/MapBox/MarkerIcons.tsx +++ b/src/client/views/nodes/MapBox/MarkerIcons.tsx @@ -17,8 +17,6 @@ import { faHouse, faLandmark, faLocationDot, - faLocationPin, - faMapPin, faMasksTheater, faMugSaucer, faPersonHiking, @@ -65,6 +63,7 @@ export class MarkerIcons { iconProps.color = color; } + // eslint-disable-next-line react/jsx-props-no-spreading return ; } diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx index 189e2105d..fce52ef4b 100644 --- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx +++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx @@ -118,9 +118,9 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent * @param sidebarKey * @returns */ - sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => { + sidebarAddDocument = (docsIn: Doc | Doc[], sidebarKey?: string) => { if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar(); - const docs = doc instanceof Doc ? [doc] : doc; + const docs = docsIn instanceof Doc ? [docsIn] : docsIn; docs.forEach(doc => { let existingPin = this.allPushpins.find(pin => pin.latitude === doc.latitude && pin.longitude === doc.longitude) ?? this.selectedPin; if (doc.latitude !== undefined && doc.longitude !== undefined && !existingPin) { @@ -139,7 +139,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent } }); // add to annotation list - return this.addDocument(doc, sidebarKey); // add to sidebar list + return this.addDocument(docs, sidebarKey); // add to sidebar list }; removeMapDocument = (doc: Doc | Doc[], annotationKey?: string) => { @@ -168,7 +168,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent setupMoveUpEvents( this, e, - (e, down, delta) => + (moveEv, down, delta) => runInAction(() => { const localDelta = this._props .ScreenToLocalTransform() @@ -243,10 +243,10 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent const docView = this.DocumentView?.(); docView && DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, { - dragComplete: e => { - if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) { - e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.Document; - e.annoDragData.linkSourceDoc.followLinkZoom = false; + dragComplete: dragEv => { + if (!dragEv.aborted && dragEv.annoDragData && dragEv.annoDragData.linkSourceDoc && dragEv.annoDragData.dropDocument && dragEv.linkDocument) { + dragEv.annoDragData.linkSourceDoc.followLinkToggle = dragEv.annoDragData.dropDocument.annotationOn === this.Document; + dragEv.annoDragData.linkSourceDoc.followLinkZoom = false; } }, }); @@ -680,23 +680,23 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent setupMoveUpEvents( e, e, - e => { + moveEv => { if (!dragClone) { dragClone = this._dragRef.current?.cloneNode(true) as HTMLDivElement; dragClone.style.position = 'absolute'; dragClone.style.zIndex = '10000'; DragManager.Root().appendChild(dragClone); } - dragClone.style.transform = `translate(${e.clientX - 15}px, ${e.clientY - 15}px)`; + dragClone.style.transform = `translate(${moveEv.clientX - 15}px, ${moveEv.clientY - 15}px)`; return false; }, - e => { + upEv => { if (!dragClone) return; DragManager.Root().removeChild(dragClone); - let target = document.elementFromPoint(e.x, e.y); + let target = document.elementFromPoint(upEv.x, upEv.y); while (target) { if (target === this._ref.current) { - const cpt = this.ScreenToLocalBoxXf().transformPoint(e.clientX, e.clientY); + const cpt = this.ScreenToLocalBoxXf().transformPoint(upEv.clientX, upEv.clientY); const x = cpt[0] - (this._props.PanelWidth() - this.sidebarWidth()) / 2; const y = cpt[1] - 32 /* height of search bar */ - this._props.PanelHeight() / 2; const location = this._bingMap.current.tryPixelToLocation(new this.MicrosoftMaps.Point(x, y)); @@ -706,7 +706,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent target = target.parentElement; } }, - e => { + () => { const createPin = () => this.createPushpin(this.Document.latitude, this.Document.longitude, this.Document.map); if (this.bingSearchBarContents) { this.bingSearch().then(createPin); diff --git a/src/client/views/nodes/RadialMenu.tsx b/src/client/views/nodes/RadialMenu.tsx index 16450c359..48da4937a 100644 --- a/src/client/views/nodes/RadialMenu.tsx +++ b/src/client/views/nodes/RadialMenu.tsx @@ -6,22 +6,57 @@ import { RadialMenuItem, RadialMenuProps } from './RadialMenuItem'; @observer export class RadialMenu extends React.Component { + // eslint-disable-next-line no-use-before-define static Instance: RadialMenu; static readonly buffer = 20; + @observable private _mouseX: number = -1; + @observable private _mouseY: number = -1; + @observable private _shouldDisplay: boolean = false; + @observable private _mouseDown: boolean = false; + @observable private _closest: number = -1; + @observable private _pageX: number = 0; + @observable private _pageY: number = 0; + @observable _display: boolean = false; + @observable private _yRelativeToTop: boolean = true; + @observable private _items: Array = []; + private _reactionDisposer?: IReactionDisposer; + constructor(props: any) { super(props); makeObservable(this); RadialMenu.Instance = this; } - @observable private _mouseX: number = -1; - @observable private _mouseY: number = -1; - @observable private _shouldDisplay: boolean = false; - @observable private _mouseDown: boolean = false; - private _reactionDisposer?: IReactionDisposer; + componentDidMount() { + document.addEventListener('pointerdown', this.onPointerDown); + document.addEventListener('pointerup', this.onPointerUp); + this.previewcircle(); + this._reactionDisposer = reaction( + () => this._shouldDisplay, + () => + this._shouldDisplay && + !this._mouseDown && + runInAction(() => { + this._display = true; + }) + ); + } - public used: boolean = false; + componentDidUpdate() { + this.previewcircle(); + } + componentWillUnmount() { + document.removeEventListener('pointerdown', this.onPointerDown); + + document.removeEventListener('pointerup', this.onPointerUp); + this._reactionDisposer && this._reactionDisposer(); + } + + @computed get menuItems() { + // eslint-disable-next-line react/jsx-props-no-spreading + return this._items.map((item, index) => ); + } catchTouch = (te: React.TouchEvent) => { te.stopPropagation(); @@ -33,13 +68,9 @@ export class RadialMenu extends React.Component { this._mouseDown = true; this._mouseX = e.clientX; this._mouseY = e.clientY; - this.used = false; document.addEventListener('pointermove', this.onPointerMove); }; - @observable - private _closest: number = -1; - @action onPointerMove = (e: PointerEvent) => { const curX = e.clientX; @@ -65,7 +96,6 @@ export class RadialMenu extends React.Component { }; @action onPointerUp = (e: PointerEvent) => { - this.used = true; this._mouseDown = false; const curX = e.clientX; const curY = e.clientY; @@ -78,86 +108,6 @@ export class RadialMenu extends React.Component { this._items[this._closest].event(); } }; - componentWillUnmount() { - document.removeEventListener('pointerdown', this.onPointerDown); - - document.removeEventListener('pointerup', this.onPointerUp); - this._reactionDisposer && this._reactionDisposer(); - } - - @action - componentDidMount() { - document.addEventListener('pointerdown', this.onPointerDown); - document.addEventListener('pointerup', this.onPointerUp); - this.previewcircle(); - this._reactionDisposer = reaction( - () => this._shouldDisplay, - () => this._shouldDisplay && !this._mouseDown && runInAction(() => (this._display = true)) - ); - } - - componentDidUpdate = () => { - this.previewcircle(); - }; - - @observable private _pageX: number = 0; - @observable private _pageY: number = 0; - @observable _display: boolean = false; - @observable private _yRelativeToTop: boolean = true; - - @observable private _width: number = 0; - @observable private _height: number = 0; - - getItems() { - return this._items; - } - - @action - addItem(item: RadialMenuProps) { - if (this._items.indexOf(item) === -1) { - this._items.push(item); - } - } - - @observable - private _items: Array = []; - - @action - displayMenu = (x: number, y: number) => { - //maxX and maxY will change if the UI/font size changes, but will work for any amount - //of items added to the menu - this._mouseX = x; - this._mouseY = y; - this._shouldDisplay = true; - }; - // @computed - // get pageX() { - // const x = this._pageX; - // if (x < 0) { - // return 0; - // } - // const width = this._width; - // if (x + width > window.innerWidth - RadialMenu.buffer) { - // return window.innerWidth - RadialMenu.buffer - width; - // } - // return x; - // } - // @computed - // get pageY() { - // const y = this._pageY; - // if (y < 0) { - // return 0; - // } - // const height = this._height; - // if (y + height > window.innerHeight - RadialMenu.buffer) { - // return window.innerHeight - RadialMenu.buffer - height; - // } - // return y; - // } - - @computed get menuItems() { - return this._items.map((item, index) => ); - } @action closeMenu = () => { @@ -166,14 +116,6 @@ export class RadialMenu extends React.Component { this._shouldDisplay = false; }; - @action - openMenu = (x: number, y: number) => { - this._pageX = x; - this._pageY = y; - this._shouldDisplay; - this._display = true; - }; - @action clearItems() { this._items = []; diff --git a/src/client/views/nodes/RadialMenuItem.tsx b/src/client/views/nodes/RadialMenuItem.tsx index 91dc37d34..6f10e7b65 100644 --- a/src/client/views/nodes/RadialMenuItem.tsx +++ b/src/client/views/nodes/RadialMenuItem.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/require-default-props */ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { observer } from 'mobx-react'; @@ -53,6 +54,7 @@ export class RadialMenuItem extends React.Component { case 2: color = 'lightgray'; break; + default: } if (circlemax % 3 === 1 && circlemin === circlemax - 1) { color = '#c2c2c5'; @@ -80,7 +82,6 @@ export class RadialMenuItem extends React.Component { const avg = (circlemin / circlemax + (circlemin + 1) / circlemax) / 2; const degrees = 360 * avg; const x = 100 * Math.cos((degrees * Math.PI) / 180); - const y = -125 * Math.sin((degrees * Math.PI) / 180); return x; } @@ -91,7 +92,6 @@ export class RadialMenuItem extends React.Component { this.props.max ? (circlemax = this.props.max) : null; const avg = (circlemin / circlemax + (circlemin + 1) / circlemax) / 2; const degrees = 360 * avg; - const x = 125 * Math.cos((degrees * Math.PI) / 180); const y = -100 * Math.sin((degrees * Math.PI) / 180); return y; } diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx index 1bb2b7c84..62798bc2f 100644 --- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx +++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx @@ -1,31 +1,33 @@ +/* eslint-disable react/no-array-index-key */ +/* eslint-disable react/require-default-props */ import * as React from 'react'; -import { useEffect, useState, useCallback, useRef } from "react" -import "./ProgressBar.scss" +import { useEffect, useState, useRef } from 'react'; +import './ProgressBar.scss'; import { MediaSegment } from './RecordingView'; interface ProgressBarProps { - videos: MediaSegment[], - setVideos: React.Dispatch>, - orderVideos: boolean, - progress: number, - recording: boolean, - doUndo: boolean, - setCanUndo?: React.Dispatch>, + videos: MediaSegment[]; + setVideos: React.Dispatch>; + orderVideos: boolean; + progress: number; + recording: boolean; + doUndo: boolean; + setCanUndo?: React.Dispatch>; } interface SegmentBox { - endTime: number, - startTime: number, - order: number, + endTime: number; + startTime: number; + order: number; } interface CurrentHover { - index: number, - minX: number, - maxX: number + index: number; + minX: number; + maxX: number; } export function ProgressBar(props: ProgressBarProps) { - const progressBarRef = useRef(null) + const progressBarRef = useRef(null); // the actual list of JSX elements rendered as segments const [segments, setSegments] = useState([]); @@ -47,8 +49,6 @@ export function ProgressBar(props: ProgressBarProps) { // update the canUndo props based on undo stack useEffect(() => props.setCanUndo?.(undoStack.length > 0), [undoStack.length]); - // useEffect for undo - brings back the most recently deleted segment - useEffect(() => handleUndo(), [props.doUndo]) const handleUndo = () => { // get the last element from the undo if it exists if (undoStack.length === 0) return; @@ -59,27 +59,36 @@ export function ProgressBar(props: ProgressBarProps) { // update the removed time and place element back into ordered setTotalRemovedTime(prevRemoved => prevRemoved - (last.endTime - last.startTime)); setOrdered(prevOrdered => [...prevOrdered, last]); - } + }; + // useEffect for undo - brings back the most recently deleted segment + useEffect(() => handleUndo(), [props.doUndo]); // useEffect for recording changes - changes style to disabled and adds the "expanding-segment" useEffect(() => { // get segments segment's html using it's id -> make them appeared disabled (or enabled) - segments.forEach((seg) => document.getElementById(seg.props.id)?.classList.toggle('segment-disabled', props.recording)); + segments.forEach(seg => document.getElementById(seg.props.id)?.classList.toggle('segment-disabled', props.recording)); progressBarRef.current?.classList.toggle('progressbar-disabled', props.recording); if (props.recording) - setSegments(prevSegments => [...prevSegments,
{props.videos.length + 1}
]); - }, [props.recording]) - + setSegments(prevSegments => [ + ...prevSegments, +
+ {props.videos.length + 1} +
, + ]); + }, [props.recording]); // useEffect that updates the segmentsJSX, which is rendered // only updated when ordered is updated or if the user is dragging around a segment useEffect(() => { const totalTime = props.progress * 1000 - totalRemovedTime; - const segmentsJSX = ordered.map((seg, i) => -
{seg.order + 1}
); + const segmentsJSX = ordered.map((seg, i) => ( +
+ {seg.order + 1} +
+ )); - setSegments(segmentsJSX) + setSegments(segmentsJSX); }, [dragged, ordered]); // useEffect for dragged - update the cursor to be grabbing while grabbing @@ -89,14 +98,14 @@ export function ProgressBar(props: ProgressBarProps) { // to imporve performance, only want to update the CSS width, not re-render the whole JSXList useEffect(() => { - if (!props.recording) return + if (!props.recording) return; const totalTime = props.progress * 1000 - totalRemovedTime; let remainingTime = totalTime; segments.forEach((seg, i) => { // for the last segment, we need to set that directly if (i === segments.length - 1) return; // update remaining time - remainingTime -= (ordered[i].endTime - ordered[i].startTime); + remainingTime -= ordered[i].endTime - ordered[i].startTime; // update the width for this segment const htmlId = seg.props.id; @@ -106,8 +115,7 @@ export function ProgressBar(props: ProgressBarProps) { // update the width of the expanding segment using the remaining time const segExapandHtml = document.getElementById('segment-expanding'); - if (segExapandHtml) - segExapandHtml.style.width = ordered.length === 0 ? '100%' : `${(remainingTime / totalTime) * 100}%`; + if (segExapandHtml) segExapandHtml.style.width = ordered.length === 0 ? '100%' : `${(remainingTime / totalTime) * 100}%`; }, [props.progress]); // useEffect for props.videos - update the ordered array when a new video is added @@ -120,9 +128,7 @@ export function ProgressBar(props: ProgressBarProps) { // in this case, a new video is added -> push it onto ordered if (order >= ordered.length) { const { endTime, startTime } = props.videos.lastElement(); - setOrdered(prevOrdered => { - return [...prevOrdered, { endTime, startTime, order }]; - }); + setOrdered(prevOrdered => [...prevOrdered, { endTime, startTime, order }]); } // in this case, a video is removed @@ -132,7 +138,7 @@ export function ProgressBar(props: ProgressBarProps) { }, [props.videos]); // useEffect for props.orderVideos - matched the order array with the videos array before the export - useEffect(() => props.setVideos(vids => ordered.map((seg) => vids[seg.order])), [props.orderVideos]); + useEffect(() => props.setVideos(vids => ordered.map(seg => vids[seg.order])), [props.orderVideos]); // useEffect for removed - handles logic for removing a segment useEffect(() => { @@ -151,36 +157,68 @@ export function ProgressBar(props: ProgressBarProps) { // returns the new currentHover based on the new index const updateCurrentHover = (segId: number): CurrentHover | null => { // get the segId of the segment that will become the new bounding area - const rect = progressBarRef.current?.children[segId].getBoundingClientRect() - if (rect == null) return null + const rect = progressBarRef.current?.children[segId].getBoundingClientRect(); + if (rect == null) return null; return { index: segId, minX: rect.x, maxX: rect.x + rect.width, - } - } + }; + }; + + const swapSegments = (oldIndex: number, newIndex: number) => { + if (newIndex == null) return; + setOrdered(prevOrdered => { + const temp = { ...prevOrdered[oldIndex] }; + prevOrdered[oldIndex] = prevOrdered[newIndex]; + prevOrdered[newIndex] = temp; + return prevOrdered; + }); + // update visually where the segment is hovering over + setDragged(newIndex); + }; + + // functions for the floating segment that tracks the cursor while grabbing it + const initDetachSegment = (dot: HTMLDivElement, rect: DOMRect) => { + dot.classList.add('segment-selected'); + dot.style.transitionDuration = '0s'; + dot.style.position = 'absolute'; + dot.style.zIndex = '999'; + dot.style.width = `${rect.width}px`; + dot.style.height = `${rect.height}px`; + dot.style.left = `${rect.x}px`; + dot.style.top = `${rect.y}px`; + dot.draggable = false; + document.body.append(dot); + }; + const followCursor = (event: PointerEvent, dot: HTMLDivElement): void => { + // event.stopPropagation() + const { width, height } = dot.getBoundingClientRect(); + dot.style.left = `${event.clientX - width / 2}px`; + dot.style.top = `${event.clientY - height / 2}px`; + }; // pointerdown event for the progress bar - const onPointerDown = (e: React.PointerEvent) => { - // don't move the videobox element - e.stopPropagation(); + const onPointerDown = (e: React.PointerEvent) => { + // don't move the videobox element + e.stopPropagation(); // if recording, do nothing - if (props.recording) return; + if (props.recording) return; // get the segment the user clicked on to be dragged - const clickedSegment = e.target as HTMLDivElement & EventTarget + const clickedSegment = e.target as HTMLDivElement & EventTarget; // get the profess bar ro add event listeners // don't do anything if null - const progressBar = progressBarRef.current - if (progressBar == null || clickedSegment.id === progressBar.id) return + const progressBar = progressBarRef.current; + if (progressBar == null || clickedSegment.id === progressBar.id) return; // if holding shift key, let's remove that segment if (e.shiftKey) { const segId = parseInt(clickedSegment.id.split('-')[1]); setRemoved(segId); - return + return; } // if holding ctrl key and click, let's undo that segment #hiddenfeature lol @@ -192,26 +230,26 @@ export function ProgressBar(props: ProgressBarProps) { // if we're here, the user is dragging a segment around // let the progress bar capture all the pointer events until the user releases (pointerUp) const ptrId = e.pointerId; - progressBar.setPointerCapture(ptrId) + progressBar.setPointerCapture(ptrId); - const rect = clickedSegment.getBoundingClientRect() - // id for segment is like 'segment-1' or 'segment-10', + const rect = clickedSegment.getBoundingClientRect(); + // id for segment is like 'segment-1' or 'segment-10', // so this works to get the id - const segId = parseInt(clickedSegment.id.split('-')[1]) + const segId = parseInt(clickedSegment.id.split('-')[1]); // set the selected segment to be the one dragged - setDragged(segId) + setDragged(segId); - // this is the logic for storing the lower X bound and upper X bound to know + // this is the logic for storing the lower X bound and upper X bound to know // whether a swap is needed between two segments let currentHover: CurrentHover = { index: segId, minX: rect.x, maxX: rect.x + rect.width, - } + }; // create the floating segment that tracks the cursor - const detchedSegment = document.createElement("div") - initDeatchSegment(detchedSegment, rect); + const detchedSegment = document.createElement('div'); + initDetachSegment(detchedSegment, rect); const updateSegmentOrder = (event: PointerEvent): void => { event.stopPropagation(); @@ -219,6 +257,7 @@ export function ProgressBar(props: ProgressBarProps) { // this fixes a bug where pointerup doesn't fire while cursor is upped while being dragged if (!progressBar.hasPointerCapture(ptrId)) { + // eslint-disable-next-line no-use-before-define placeSegmentandCleanup(); return; } @@ -228,24 +267,23 @@ export function ProgressBar(props: ProgressBarProps) { const curX = event.clientX; // handle the left bound if (curX < currentHover.minX && currentHover.index > 0) { - swapSegments(currentHover.index, currentHover.index - 1) - currentHover = updateCurrentHover(currentHover.index - 1) ?? currentHover + swapSegments(currentHover.index, currentHover.index - 1); + currentHover = updateCurrentHover(currentHover.index - 1) ?? currentHover; } // handle the right bound else if (curX > currentHover.maxX && currentHover.index < segments.length - 1) { - swapSegments(currentHover.index, currentHover.index + 1) - currentHover = updateCurrentHover(currentHover.index + 1) ?? currentHover + swapSegments(currentHover.index, currentHover.index + 1); + currentHover = updateCurrentHover(currentHover.index + 1) ?? currentHover; } - } + }; // handles when the user is done dragging the segment (pointerUp) const placeSegmentandCleanup = (event?: PointerEvent): void => { event?.stopPropagation(); event?.preventDefault(); // if they put the segment outside of the bounds, remove it - if (event && (event.clientX < 0 || event.clientX > document.body.clientWidth || event.clientY < 0 || event.clientY > document.body.clientHeight)) - setRemoved(currentHover.index); - + if (event && (event.clientX < 0 || event.clientX > document.body.clientWidth || event.clientY < 0 || event.clientY > document.body.clientHeight)) setRemoved(currentHover.index); + // remove the update event listener for pointermove progressBar.removeEventListener('pointermove', updateSegmentOrder); // remove the floating segment from the DOM @@ -253,49 +291,16 @@ export function ProgressBar(props: ProgressBarProps) { // dragged is -1 is equiv to nothing being dragged, so the normal state // so this will place the segment in it's location and update the segment bar setDragged(-1); - } + }; // event listeners that allow the user to drag and release the floating segment progressBar.addEventListener('pointermove', updateSegmentOrder); progressBar.addEventListener('pointerup', placeSegmentandCleanup, { once: true }); - } - - const swapSegments = (oldIndex: number, newIndex: number) => { - if (newIndex == null) return; - setOrdered(prevOrdered => { - const temp = { ...prevOrdered[oldIndex] } - prevOrdered[oldIndex] = prevOrdered[newIndex] - prevOrdered[newIndex] = temp - return prevOrdered - }); - // update visually where the segment is hovering over - setDragged(newIndex); - } - - // functions for the floating segment that tracks the cursor while grabbing it - const initDeatchSegment = (dot: HTMLDivElement, rect: DOMRect) => { - dot.classList.add("segment-selected"); - dot.style.transitionDuration = '0s'; - dot.style.position = 'absolute'; - dot.style.zIndex = '999'; - dot.style.width = `${rect.width}px`; - dot.style.height = `${rect.height}px`; - dot.style.left = `${rect.x}px`; - dot.style.top = `${rect.y}px`; - dot.draggable = false; - document.body.append(dot); - } - const followCursor = (event: PointerEvent, dot: HTMLDivElement): void => { - // event.stopPropagation() - const { width, height } = dot.getBoundingClientRect(); - dot.style.left = `${event.clientX - width / 2}px`; - dot.style.top = `${event.clientY - height / 2}px`; - } - + }; return (
{segments}
- ) -} \ No newline at end of file + ); +} diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index 7d123d90c..2bc749e1b 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -44,18 +44,11 @@ export class RecordingBox extends ViewBoxBaseComponent() { } @observable result: Upload.AccessPathInfo | undefined = undefined; - @observable videoDuration: number | undefined = undefined; - - @action - setVideoDuration = (duration: number) => { - this.videoDuration = duration; - }; @action setResult = (info: Upload.AccessPathInfo, presentation?: Presentation) => { this.result = info; this.dataDoc.type = DocumentType.VID; - this.dataDoc[this.fieldKey + '_duration'] = this.videoDuration; this.dataDoc.layout = VideoBox.LayoutString(this.fieldKey); this.dataDoc[this._props.fieldKey] = new VideoField(this.result.accessPaths.client); @@ -194,15 +187,7 @@ export class RecordingBox extends ViewBoxBaseComponent() { render() { return (
- {!this.result && ( - - )} + {!this.result && }
); } diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx index 9c05a3e94..b8451fe60 100644 --- a/src/client/views/nodes/RecordingBox/RecordingView.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx @@ -1,3 +1,6 @@ +/* eslint-disable jsx-a11y/label-has-associated-control */ +/* eslint-disable react/button-has-type */ +/* eslint-disable jsx-a11y/control-has-associated-label */ import * as React from 'react'; import { useEffect, useRef, useState } from 'react'; import { IconContext } from 'react-icons'; @@ -19,19 +22,18 @@ export interface MediaSegment { interface IRecordingViewProps { setResult: (info: Upload.AccessPathInfo, presentation?: Presentation) => void; - setDuration: (seconds: number) => void; id: string; getControls: (record: () => void, pause: () => void, finish: () => void) => void; forceTrackScreen: boolean; } const MAXTIME = 100000; +const iconVals = { color: '#cc1c08', className: 'video-edit-buttons' }; export function RecordingView(props: IRecordingViewProps) { const [recording, setRecording] = useState(false); const recordingTimerRef = useRef(0); const [recordingTimer, setRecordingTimer] = useState(0); // unit is 0.01 second - const [playing, setPlaying] = useState(false); const [progress, setProgress] = useState(0); // acts as a "refresh state" to tell progressBar when to undo @@ -62,7 +64,7 @@ export function RecordingView(props: IRecordingViewProps) { useEffect(() => { if (finished) { // make the total presentation that'll match the concatted video - let concatPres = (trackScreen || props.forceTrackScreen) && TrackMovements.Instance.concatPresentations(videos.map(v => v.presentation as Presentation)); + const concatPres = (trackScreen || props.forceTrackScreen) && TrackMovements.Instance.concatPresentations(videos.map(v => v.presentation as Presentation)); // this async function uses the server to create the concatted video and then sets the result to it's accessPaths (async () => { @@ -100,16 +102,16 @@ export function RecordingView(props: IRecordingViewProps) { return () => clearInterval(interval); }, [recording]); + const setVideoProgressHelper = (curProgrss: number) => { + const newProgress = (curProgrss / MAXTIME) * 100; + setProgress(newProgress); + }; + useEffect(() => { setVideoProgressHelper(recordingTimer); recordingTimerRef.current = recordingTimer; }, [recordingTimer]); - const setVideoProgressHelper = (progress: number) => { - const newProgress = (progress / MAXTIME) * 100; - setProgress(newProgress); - }; - const startShowingStream = async (mediaConstraints = DEFAULT_MEDIA_CONSTRAINTS) => { const stream = await navigator.mediaDevices.getUserMedia(mediaConstraints); @@ -131,7 +133,7 @@ export function RecordingView(props: IRecordingViewProps) { if (event.data.size > 0) videoChunks.push(event.data); }; - videoRecorder.current.onstart = (event: any) => { + videoRecorder.current.onstart = () => { setRecording(true); // start the recording api when the video recorder starts (trackScreen || props.forceTrackScreen) && TrackMovements.Instance.start(); @@ -149,7 +151,7 @@ export function RecordingView(props: IRecordingViewProps) { // depending on if a presenation exists, add it to the video const presentation = TrackMovements.Instance.yieldPresentation(); - setVideos(videos => [...videos, presentation != null && (trackScreen || props.forceTrackScreen) ? { ...nextVideo, presentation } : nextVideo]); + setVideos(theVideos => [...theVideos, presentation != null && (trackScreen || props.forceTrackScreen) ? { ...nextVideo, presentation } : nextVideo]); } // reset the temporary chunks @@ -186,7 +188,7 @@ export function RecordingView(props: IRecordingViewProps) { e, returnTrue, returnFalse, - e => { + () => { // start recording if not already recording if (!videoRecorder.current || videoRecorder.current.state === 'inactive') record(); @@ -202,14 +204,8 @@ export function RecordingView(props: IRecordingViewProps) { setDoUndo(prev => !prev); }; - const handleOnTimeUpdate = () => { - playing && setVideoProgressHelper(videoElementRef.current!.currentTime); - }; - const millisecondToMinuteSecond = (milliseconds: number) => { - const toTwoDigit = (digit: number) => { - return String(digit).length == 1 ? '0' + digit : digit; - }; + const toTwoDigit = (digit: number) => (String(digit).length === 1 ? '0' + digit : digit); const minutes = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((milliseconds % (1000 * 60)) / 1000); return toTwoDigit(minutes) + ' : ' + toTwoDigit(seconds); @@ -219,10 +215,11 @@ export function RecordingView(props: IRecordingViewProps) { props.getControls(record, pause, finish); }, []); + const iconUndoVals = React.useMemo(() => ({ color: 'grey', className: 'video-edit-buttons', style: { display: canUndo ? 'inherit' : 'none' } }), []); return (
-