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/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 +- 51 files changed, 1154 insertions(+), 1183 deletions(-) (limited to 'src/client/views/nodes') 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 (
-