Skip to content

Commit 9cd66c2

Browse files
committed
fix(viewer): avoid flicker on dropTarget=document upload box
ref #131 (comment)
1 parent e8ccb83 commit 9cd66c2

File tree

2 files changed

+39
-36
lines changed

2 files changed

+39
-36
lines changed

packages/viewer/src/ui/components/report-upload-box.jsx

+37-36
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
import {h, Fragment} from 'preact';
88
import './report-upload-box.css';
99
import {LhrViewerButton} from './lhci-components';
10-
import {useState, useEffect} from 'preact/hooks';
10+
import {useState, useEffect, useRef} from 'preact/hooks';
1111
import clsx from 'clsx';
1212

1313
/** @typedef {import('../app.jsx').ToastMessage} ToastMessage */
1414
/** @typedef {import('../app.jsx').ReportData} ReportData */
1515
/** @typedef {'filename'|'hostname'|'pathname'|'path'|'timestamp-hostname'|'timestamp-pathname'} DisplayType */
1616
/** @typedef {{variant: 'base'|'compare', displayType: DisplayType, report: ReportData|undefined, setReport: (d: ReportData) => void, addToast: (t: ToastMessage) => void, showOpenLhrLink?: boolean, dragTarget?: 'self' | 'document'}} ReportUploadBoxProps */
17-
/** @typedef {{isDragging: boolean, dragTarget: HTMLElement|undefined}} DragData */
17+
/** @typedef {HTMLElement|undefined} DragData */
1818

1919
/** @param {string} s @return {LH.Result|Error} */
2020
export function parseStringAsLhr(s) {
@@ -91,60 +91,60 @@ function handleFileInput(props, fileList) {
9191
});
9292
}
9393

94-
/** @param {ReportUploadBoxProps} props @param {DragData} dragData @param {(d: DragData) => void} setDragData @param {Event} e */
95-
function handleDragEnter(props, dragData, setDragData, e) {
94+
/** @param {ReportUploadBoxProps} props @param {import('preact/hooks').Ref<DragData>} dragTargetRef @param {(b: boolean) => void} setIsDragging @param {Event} e */
95+
function handleDragEnter(props, dragTargetRef, setIsDragging, e) {
9696
if (!(e.target instanceof HTMLElement)) return;
97-
if (dragData.isDragging && dragData.dragTarget === e.target) return;
9897
e.stopPropagation();
9998
e.preventDefault();
100-
setDragData({isDragging: true, dragTarget: e.target});
99+
setIsDragging(true);
100+
dragTargetRef.current = e.target;
101101
}
102102

103-
/** @param {ReportUploadBoxProps} props @param {DragData} dragData @param {(d: DragData) => void} setDragData @param {Event} e */
104-
function handleDragLeave(props, dragData, setDragData, e) {
105-
if (e.target !== dragData.dragTarget) return;
103+
/** @param {ReportUploadBoxProps} props @param {import('preact/hooks').Ref<DragData>} dragTargetRef @param {(b: boolean) => void} setIsDragging @param {Event} e */
104+
function handleDragLeave(props, dragTargetRef, setIsDragging, e) {
105+
if (e.target !== dragTargetRef.current) return;
106106
e.stopPropagation();
107107
e.preventDefault();
108-
setDragData({isDragging: false, dragTarget: undefined});
108+
setIsDragging(false);
109+
dragTargetRef.current = undefined;
109110
}
110111

111-
/** @param {ReportUploadBoxProps} props @param {DragData} dragData @param {(d: DragData) => void} setDragData @param {Event} e */
112-
function handleDragOver(props, dragData, setDragData, e) {
113-
if (!dragData.dragTarget) return;
112+
/** @param {ReportUploadBoxProps} props @param {import('preact/hooks').Ref<DragData>} dragTargetRef @param {(b: boolean) => void} setIsDragging @param {Event} e */
113+
function handleDragOver(props, dragTargetRef, setIsDragging, e) {
114+
if (!dragTargetRef.current) return;
114115
e.stopPropagation();
115116
e.preventDefault();
116117
}
117118

118-
/** @param {Pick<ReportUploadBoxProps, 'addToast'|'setReport'>} props @param {DragData} dragData @param {(d: DragData) => void} setDragData @param {Event} e */
119-
function handleDrop(props, dragData, setDragData, e) {
120-
if (!dragData.dragTarget) return;
119+
/** @param {Pick<ReportUploadBoxProps, 'addToast'|'setReport'>} props @param {import('preact/hooks').Ref<DragData>} dragTargetRef @param {(b: boolean) => void} setIsDragging @param {Event} e */
120+
function handleDrop(props, dragTargetRef, setIsDragging, e) {
121+
if (!dragTargetRef.current) return;
121122
if (!(e instanceof DragEvent)) return;
122123
if (!e.dataTransfer) return;
123124
e.stopPropagation();
124125
e.preventDefault();
125-
setDragData({isDragging: false, dragTarget: undefined});
126+
setIsDragging(false);
127+
dragTargetRef.current = undefined;
126128
handleFileInput(props, e.dataTransfer.files);
127129
}
128130

129131
/** @param {ReportUploadBoxProps} props */
130132
export const ReportUploadBox = props => {
131-
const [dragData, setDragData] = useState({
132-
isDragging: false,
133-
dragTarget: /** @type {HTMLElement|undefined} */ (undefined),
134-
});
133+
const [isDragging, setIsDragging] = useState(false);
134+
const dragTargetRef = useRef(/** @type {HTMLElement|undefined} */ (undefined));
135+
136+
/** @param {Event} e */
137+
const onDragEnter = e => handleDragEnter(props, dragTargetRef, setIsDragging, e);
138+
/** @param {Event} e */
139+
const onDragLeave = e => handleDragLeave(props, dragTargetRef, setIsDragging, e);
140+
/** @param {Event} e */
141+
const onDragOver = e => handleDragOver(props, dragTargetRef, setIsDragging, e);
142+
/** @param {Event} e */
143+
const onDrop = e => handleDrop(props, dragTargetRef, setIsDragging, e);
135144

136145
useEffect(() => {
137146
if (props.dragTarget !== 'document') return;
138147

139-
/** @param {Event} e */
140-
const onDragEnter = e => handleDragEnter(props, dragData, setDragData, e);
141-
/** @param {Event} e */
142-
const onDragLeave = e => handleDragLeave(props, dragData, setDragData, e);
143-
/** @param {Event} e */
144-
const onDragOver = e => handleDragOver(props, dragData, setDragData, e);
145-
/** @param {Event} e */
146-
const onDrop = e => handleDrop(props, dragData, setDragData, e);
147-
148148
document.addEventListener('dragenter', onDragEnter);
149149
document.addEventListener('dragleave', onDragLeave);
150150
document.addEventListener('dragover', onDragOver);
@@ -156,17 +156,18 @@ export const ReportUploadBox = props => {
156156
document.removeEventListener('dragover', onDragOver);
157157
document.removeEventListener('drop', onDrop);
158158
};
159-
}, [props.dragTarget, props.addToast, props.setReport, dragData, setDragData]);
159+
}, [props.dragTarget, props.addToast, props.setReport, dragTargetRef.current]);
160160

161161
return (
162162
<div
163163
className={clsx(`report-upload-box report-upload-box--${props.variant}`, {
164-
'report-upload-box--drop': dragData.isDragging,
164+
'report-upload-box--drop': isDragging,
165+
'report-upload-box--document-target': props.dragTarget === 'document',
165166
})}
166-
onDragEnter={e => handleDragEnter(props, dragData, setDragData, e)}
167-
onDragLeave={e => handleDragLeave(props, dragData, setDragData, e)}
168-
onDragOver={e => handleDragOver(props, dragData, setDragData, e)}
169-
onDrop={e => handleDrop(props, dragData, setDragData, e)}
167+
onDragEnter={onDragEnter}
168+
onDragLeave={onDragLeave}
169+
onDragOver={onDragOver}
170+
onDrop={onDrop}
170171
>
171172
<div className="report-upload-box__drop-outline">Drop your report to upload</div>
172173
<span className="report-upload-box__label">

packages/viewer/src/ui/routes/comparison/comparison.jsx

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export const ComparisonRoute = props => {
4141
<div className="comparison-header__upload">
4242
<ReportUploadBox
4343
variant="base"
44+
dragTarget="self"
4445
report={props.baseReport}
4546
setReport={props.setBaseReport}
4647
addToast={props.addToast}
@@ -49,6 +50,7 @@ export const ComparisonRoute = props => {
4950
/>
5051
<ReportUploadBox
5152
variant="compare"
53+
dragTarget="self"
5254
report={props.compareReport}
5355
setReport={props.setCompareReport}
5456
addToast={props.addToast}

0 commit comments

Comments
 (0)