Bug 1938418 - [devtools] Immediately highlight the hovered previewed token. r=devtools-reviewers,nchevobbe

This helps see that computation may be ongoing, waiting for the file parsing.

Differential Revision: https://phabricator.services.mozilla.com/D237652
This commit is contained in:
Alexandre Poirot
2025-03-04 18:14:53 +00:00
parent 72532efba9
commit e2d7cfe124
2 changed files with 69 additions and 15 deletions

View File

@@ -119,6 +119,33 @@
cursor: default; cursor: default;
} }
/**
* Use an animation in order to delay the preview-loading-token
* style 1/4s second after hovering a token.
*
* It avoids to render this intermediate style if the previews is ready
* right away.
*/
@keyframes animateLoading {
25% {
cursor: wait;
text-decoration: grey underline solid;
text-decoration-thickness: 2px;
text-underline-offset: 2px;
text-decoration-skip-ink: none;
}
100% {
cursor: wait;
text-decoration: grey underline solid;
text-decoration-thickness: 2px;
text-underline-offset: 2px;
text-decoration-skip-ink: none;
}
}
.preview-loading-token {
animation: animateLoading 1s forwards;
}
.preview-token, .preview-token,
.debug-expression.preview-token { .debug-expression.preview-token {
background-color: var(--theme-highlight-yellow); background-color: var(--theme-highlight-yellow);

View File

@@ -17,11 +17,13 @@ import { features } from "../../../utils/prefs";
const EXCEPTION_MARKER = "mark-text-exception"; const EXCEPTION_MARKER = "mark-text-exception";
const LOADING_CLASS = "preview-loading-token";
class Preview extends PureComponent { class Preview extends PureComponent {
target = null; target = null;
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { selecting: false }; this.state = { selecting: false, loading: null };
} }
static get propTypes() { static get propTypes() {
@@ -72,6 +74,18 @@ class Preview extends PureComponent {
} }
} }
componentDidUpdate(_prevProps, prevState) {
// Ensure that only one token is highlighted as "loading"
const previous = prevState.loading;
if (previous) {
previous.classList.remove(LOADING_CLASS);
}
const { loading } = this.state;
if (loading) {
loading.classList.add(LOADING_CLASS);
}
}
// Note that these events are emitted by utils/editor/tokens.js // Note that these events are emitted by utils/editor/tokens.js
onTokenEnter = async ({ target, tokenPos }) => { onTokenEnter = async ({ target, tokenPos }) => {
// Use a temporary object to uniquely identify the asynchronous processing of this user event // Use a temporary object to uniquely identify the asynchronous processing of this user event
@@ -79,6 +93,9 @@ class Preview extends PureComponent {
const tokenId = {}; const tokenId = {};
this.currentTokenId = tokenId; this.currentTokenId = tokenId;
// Immediately highlight the hovered token as "loading"
this.setState({ loading: target });
const { const {
editor, editor,
getPausedPreview, getPausedPreview,
@@ -90,24 +107,29 @@ class Preview extends PureComponent {
const isTargetException = target.closest(`.${EXCEPTION_MARKER}`); const isTargetException = target.closest(`.${EXCEPTION_MARKER}`);
let preview; let preview;
if (isTargetException) { try {
preview = await getExceptionPreview(target, tokenPos, editor); if (isTargetException) {
} preview = await getExceptionPreview(target, tokenPos, editor);
}
if (!preview && (hasSelectedTrace || isPaused) && !this.state.selecting) { if (!preview && (hasSelectedTrace || isPaused) && !this.state.selecting) {
if (hasSelectedTrace) { if (hasSelectedTrace) {
preview = await getTracerPreview(target, tokenPos, editor); preview = await getTracerPreview(target, tokenPos, editor);
} }
if (!preview && isPaused) { if (!preview && isPaused) {
preview = await getPausedPreview(target, tokenPos, editor); preview = await getPausedPreview(target, tokenPos, editor);
}
} }
} catch (e) {
// Ignore any exception and dismiss the popup (as preview will be null)
} }
// Prevent modifying state and showing this preview if we started hovering another token // Prevent modifying state and showing this preview if we started hovering another token
if (!preview || this.currentTokenId !== tokenId) { if (this.currentTokenId !== tokenId) {
return; return;
} }
this.setState({ preview });
this.setState({ loading: null, preview });
}; };
onMouseUp = () => { onMouseUp = () => {
@@ -129,14 +151,19 @@ class Preview extends PureComponent {
}; };
clearPreview = () => { clearPreview = () => {
this.setState({ preview: null }); this.setState({ loading: null, preview: null });
}; };
render() { render() {
const { preview } = this.state; if (this.state.selecting) {
if (!preview || this.state.selecting) {
return null; return null;
} }
const { preview } = this.state;
if (!preview) {
return null;
}
return React.createElement(Popup, { return React.createElement(Popup, {
preview, preview,
editor: this.props.editor, editor: this.props.editor,