set animation startTime to sync spinners for status indicators (#259)

* set animation startTime to sync spinners for status indicators

* catch animationstart (hover interaction) to restart startTime.

* use const

---------

Co-authored-by: Evan Simkowitz <esimkowitz@users.noreply.github.com>
This commit is contained in:
Mike Sawka 2024-01-29 23:48:37 -08:00 committed by GitHub
parent 1a51d93a54
commit 8f64d6a589
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -2,17 +2,19 @@ import React from "react";
import { StatusIndicatorLevel } from "../../../types/types";
import cn from "classnames";
import { ReactComponent as SpinnerIndicator } from "../../assets/icons/spinner-indicator.svg";
import { boundMethod } from "autobind-decorator";
interface PositionalIconProps {
children?: React.ReactNode;
className?: string;
onClick?: React.MouseEventHandler<HTMLDivElement>;
divRef?: React.RefObject<HTMLDivElement>;
}
export class FrontIcon extends React.Component<PositionalIconProps> {
render() {
return (
<div className={cn("front-icon", "positional-icon", this.props.className)}>
<div ref={this.props.divRef} className={cn("front-icon", "positional-icon", this.props.className)}>
<div className="positional-icon-inner">{this.props.children}</div>
</div>
);
@ -22,7 +24,11 @@ export class FrontIcon extends React.Component<PositionalIconProps> {
export class CenteredIcon extends React.Component<PositionalIconProps> {
render() {
return (
<div className={cn("centered-icon", "positional-icon", this.props.className)} onClick={this.props.onClick}>
<div
ref={this.props.divRef}
className={cn("centered-icon", "positional-icon", this.props.className)}
onClick={this.props.onClick}
>
<div className="positional-icon-inner">{this.props.children}</div>
</div>
);
@ -50,6 +56,66 @@ interface StatusIndicatorProps {
}
export class StatusIndicator extends React.Component<StatusIndicatorProps> {
iconRef: React.RefObject<HTMLDivElement> = React.createRef();
listenerAdded: boolean = false;
componentDidMount() {
this.syncSpinner();
}
componentDidUpdate() {
this.syncSpinner();
}
componentWillUnmount(): void {
if (this.iconRef.current != null && this.listenerAdded) {
const elem = this.iconRef.current;
const svgElem = elem.querySelector("svg");
if (svgElem != null) {
svgElem.removeEventListener("animationstart", this.handleAnimationStart);
}
}
}
@boundMethod
handleAnimationStart(e: AnimationEvent) {
if (this.iconRef.current == null) {
return;
}
const svgElem = this.iconRef.current.querySelector("svg");
if (svgElem == null) {
return;
}
const animArr = svgElem.getAnimations();
if (animArr == null || animArr.length == 0) {
return;
}
animArr[0].startTime = 0;
}
syncSpinner() {
if (!this.props.runningCommands) {
return;
}
if (this.iconRef.current == null) {
return;
}
const elem = this.iconRef.current;
const svgElem = elem.querySelector("svg");
if (svgElem == null) {
return;
}
if (!this.listenerAdded) {
svgElem.addEventListener("animationstart", this.handleAnimationStart);
this.listenerAdded = true;
}
const animArr = svgElem.getAnimations();
if (animArr == null || animArr.length == 0) {
return;
}
animArr[0].startTime = 0;
}
render() {
const { level, className, runningCommands } = this.props;
let statusIndicator = null;
@ -67,7 +133,7 @@ export class StatusIndicator extends React.Component<StatusIndicatorProps> {
break;
}
statusIndicator = (
<CenteredIcon className={cn(className, levelClass, "status-indicator")}>
<CenteredIcon divRef={this.iconRef} className={cn(className, levelClass, "status-indicator")}>
<SpinnerIndicator className={runningCommands ? "spin" : null} />
</CenteredIcon>
);