gave the pulse box better animation pacing so the colors don't change in the middle of a pulse
This commit is contained in:
parent
62763dd291
commit
575c84246c
5 changed files with 50 additions and 91 deletions
|
|
@ -1,56 +1,76 @@
|
||||||
import { Box, BoxProps, Theme } from "@mui/material";
|
import { Box, BoxProps, Theme } from "@mui/material";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { forwardRef, ReactNode } from "react";
|
import { forwardRef, ReactNode, useCallback, useState } from "react";
|
||||||
|
|
||||||
const useStyles = makeStyles((_theme: Theme) => ({
|
const useStyles = makeStyles((_theme: Theme) => ({
|
||||||
pulseWrapper: {
|
pulseWrapper: {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
display: 'block', // Changed to block to respect Box margins
|
display: 'block',
|
||||||
width: 'fit-content', // Shrinks to child width
|
width: 'fit-content',
|
||||||
'&::after': {
|
},
|
||||||
content: '""',
|
pulseRing: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
borderRadius: 'inherit',
|
borderRadius: 'inherit',
|
||||||
animation: '$pulse 1.75s infinite ease-in-out',
|
pointerEvents: 'none',
|
||||||
pointerEvents: 'none',
|
boxSizing: 'border-box',
|
||||||
boxSizing: 'border-box',
|
animation: '$pulse 4.4s infinite ease-in-out',
|
||||||
transition: "border-color 0.35s ease-in-out",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
'@keyframes pulse': {
|
'@keyframes pulse': {
|
||||||
from: {
|
/* pulse, pause, pulse, pause - CONSISTENT: both pauses equal, both pulses equal */
|
||||||
boxShadow: '0 0 0 2px var(--pulse-color)',
|
/* Pulse 1 (0-42%) */
|
||||||
opacity: 1,
|
'0%': { boxShadow: '0 0 0 2px var(--pulse-color)', opacity: 0 },
|
||||||
},
|
'2%': { boxShadow: '0 0 0 2px var(--pulse-color)', opacity: 1 },
|
||||||
to: {
|
'40%': { boxShadow: '0 0 0 14px var(--pulse-color)', opacity: 0 },
|
||||||
boxShadow: '0 0 0 14px var(--pulse-color)',
|
'42%': { boxShadow: '0 0 0 2px var(--pulse-color)', opacity: 0 },
|
||||||
opacity: 0,
|
/* Pause 1 (42-50%) */
|
||||||
},
|
'50%': { boxShadow: '0 0 0 2px var(--pulse-color)', opacity: 0 },
|
||||||
|
/* Pulse 2 (50-92%) */
|
||||||
|
'52%': { boxShadow: '0 0 0 2px var(--pulse-color)', opacity: 1 },
|
||||||
|
'90%': { boxShadow: '0 0 0 14px var(--pulse-color)', opacity: 0 },
|
||||||
|
'92%': { boxShadow: '0 0 0 2px var(--pulse-color)', opacity: 0 },
|
||||||
|
/* Pause 2 (92-100%) - same length as Pause 1, color change at iteration */
|
||||||
|
'100%': { boxShadow: '0 0 0 2px var(--pulse-color)', opacity: 0 },
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface Props extends BoxProps {
|
interface Props extends BoxProps {
|
||||||
color: string;
|
color: string;
|
||||||
|
colors?: string[];
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PulseBox = forwardRef<HTMLDivElement, Props>((props, ref) => {
|
const PulseBox = forwardRef<HTMLDivElement, Props>((props, ref) => {
|
||||||
const { color, children, className, ...boxProps } = props;
|
const { color, colors: colorsProp, children, className, ...boxProps } = props;
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const colors = colorsProp && colorsProp.length > 0 ? colorsProp : [color];
|
||||||
|
const [colorIndex, setColorIndex] = useState(0);
|
||||||
|
const displayColor = colors[colorIndex % colors.length] ?? color;
|
||||||
|
|
||||||
|
const onAnimationIteration = useCallback(() => {
|
||||||
|
if (colors.length > 1) {
|
||||||
|
setColorIndex((i) => (i + 1) % colors.length);
|
||||||
|
}
|
||||||
|
}, [colors.length]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
ref={ref}
|
ref={ref}
|
||||||
{...boxProps}
|
{...boxProps}
|
||||||
className={classNames([classes.pulseWrapper, className])}
|
className={classNames([classes.pulseWrapper, className])}
|
||||||
style={{ '--pulse-color': color } as React.CSSProperties}
|
style={{ '--pulse-color': displayColor } as React.CSSProperties}
|
||||||
>
|
>
|
||||||
|
<span
|
||||||
|
className={classes.pulseRing}
|
||||||
|
aria-hidden
|
||||||
|
onAnimationIteration={onAnimationIteration}
|
||||||
|
/>
|
||||||
{children}
|
{children}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -57,21 +57,6 @@ export default function StatusDust(props: Props) {
|
||||||
const colors = or(device.status.sen5x?.overlays.map(overlay => overlay.color), []);
|
const colors = or(device.status.sen5x?.overlays.map(overlay => overlay.color), []);
|
||||||
const messages = or(device.status.sen5x?.overlays.map(overlay => overlay.message), []);
|
const messages = or(device.status.sen5x?.overlays.map(overlay => overlay.message), []);
|
||||||
|
|
||||||
const [color, setColor] = useState(colors.length > 0 ? colors[0] : "");
|
|
||||||
const [, setColorIndex] = useState(0)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
setColorIndex(prevIndex => {
|
|
||||||
const newIndex = prevIndex >= colors.length - 1 ? 0 : prevIndex + 1;
|
|
||||||
setColor(colors[newIndex]); // Use the new index here
|
|
||||||
return newIndex;
|
|
||||||
});
|
|
||||||
}, 7500);
|
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// const [currentIndex, setCurrentIndex] = useState(0);
|
// const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
|
|
||||||
// Auto-cycle through measurements every 3 seconds
|
// Auto-cycle through measurements every 3 seconds
|
||||||
|
|
@ -142,7 +127,7 @@ export default function StatusDust(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip title={tooltip()}>
|
<Tooltip title={tooltip()}>
|
||||||
<PulseBox color={color} className={classes.box} >
|
<PulseBox color={colors[0] ?? ""} colors={colors} className={classes.box} >
|
||||||
<Grid2 container direction="column" >
|
<Grid2 container direction="column" >
|
||||||
<Grid2 container direction="column"
|
<Grid2 container direction="column"
|
||||||
className={`${classes.carouselItem} ${classes.active }`}
|
className={`${classes.carouselItem} ${classes.active }`}
|
||||||
|
|
|
||||||
|
|
@ -85,23 +85,8 @@ export default function StatusGas(props: Props) {
|
||||||
messages = or(device.status[gasType]?.overlays?.map(overlay => overlay.message), []);
|
messages = or(device.status[gasType]?.overlays?.map(overlay => overlay.message), []);
|
||||||
}
|
}
|
||||||
|
|
||||||
const [color, setColor] = useState(colors.length > 0 ? colors[0] : "");
|
|
||||||
const [, setColorIndex] = useState(0)
|
|
||||||
|
|
||||||
const gasTypes: ("o2" | "no2" | "co2" | "co" | "lel" | "h2s")[] = ["o2", "no2", "co2", "co", "lel", "h2s"]
|
const gasTypes: ("o2" | "no2" | "co2" | "co" | "lel" | "h2s")[] = ["o2", "no2", "co2", "co", "lel", "h2s"]
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
setColorIndex(prevIndex => {
|
|
||||||
const newIndex = prevIndex >= colors.length - 1 ? 0 : prevIndex + 1;
|
|
||||||
setColor(colors[newIndex]); // Use the new index here
|
|
||||||
return newIndex;
|
|
||||||
});
|
|
||||||
}, 7500);
|
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const [currentIndex, setCurrentIndex] = useState(0);
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
|
|
||||||
// Auto-cycle through measurements every 5 seconds
|
// Auto-cycle through measurements every 5 seconds
|
||||||
|
|
@ -183,7 +168,7 @@ export default function StatusGas(props: Props) {
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Tooltip title={tooltip()}>
|
<Tooltip title={tooltip()}>
|
||||||
<PulseBox color={color} className={gasType === "all" ? classes.boxTall : classes.box}>
|
<PulseBox color={colors[0] ?? ""} colors={colors} className={gasType === "all" ? classes.boxTall : classes.box}>
|
||||||
<Grid2 container direction="column" >
|
<Grid2 container direction="column" >
|
||||||
{ gasType === "all" &&
|
{ gasType === "all" &&
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import { Device } from "models";
|
||||||
import PulseBox from "./PulseBox";
|
import PulseBox from "./PulseBox";
|
||||||
import RelativeTimestamp from "./RelativeTimestamp";
|
import RelativeTimestamp from "./RelativeTimestamp";
|
||||||
import { or } from "utils";
|
import { or } from "utils";
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
device: Device;
|
device: Device;
|
||||||
|
|
@ -33,21 +32,6 @@ export default function StatusPlenum(props: Props) {
|
||||||
const colors = or(device.status.plenum?.overlays.map(overlay => overlay.color), []);
|
const colors = or(device.status.plenum?.overlays.map(overlay => overlay.color), []);
|
||||||
const messages = or(device.status.plenum?.overlays.map(overlay => overlay.message), []);
|
const messages = or(device.status.plenum?.overlays.map(overlay => overlay.message), []);
|
||||||
|
|
||||||
const [color, setColor] = useState(colors.length > 0 ? colors[0] : "");
|
|
||||||
const [, setColorIndex] = useState(0)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
setColorIndex(prevIndex => {
|
|
||||||
const newIndex = prevIndex >= colors.length - 1 ? 0 : prevIndex + 1;
|
|
||||||
setColor(colors[newIndex]); // Use the new index here
|
|
||||||
return newIndex;
|
|
||||||
});
|
|
||||||
}, 5000);
|
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const tooltip = () => {
|
const tooltip = () => {
|
||||||
if (messages.length === 0) return null
|
if (messages.length === 0) return null
|
||||||
if (messages.length < 2) return (
|
if (messages.length < 2) return (
|
||||||
|
|
@ -107,7 +91,7 @@ export default function StatusPlenum(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip title={tooltip()}>
|
<Tooltip title={tooltip()}>
|
||||||
<PulseBox color={color} className={classes.box} >
|
<PulseBox color={colors[0] ?? ""} colors={colors} className={classes.box} >
|
||||||
<Grid2 container direction="column" >
|
<Grid2 container direction="column" >
|
||||||
<Grid2>
|
<Grid2>
|
||||||
<Typography variant="body2" style={{ color: orange[700]}}>
|
<Typography variant="body2" style={{ color: orange[700]}}>
|
||||||
|
|
|
||||||
|
|
@ -58,21 +58,6 @@ export default function StatusSen5x(props: Props) {
|
||||||
const colors = or(device.status.sen5x?.overlays.map(overlay => overlay.color), []);
|
const colors = or(device.status.sen5x?.overlays.map(overlay => overlay.color), []);
|
||||||
const messages = or(device.status.sen5x?.overlays.map(overlay => overlay.message), []);
|
const messages = or(device.status.sen5x?.overlays.map(overlay => overlay.message), []);
|
||||||
|
|
||||||
const [color, setColor] = useState(colors.length > 0 ? colors[0] : "");
|
|
||||||
const [, setColorIndex] = useState(0)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
setColorIndex(prevIndex => {
|
|
||||||
const newIndex = prevIndex >= colors.length - 1 ? 0 : prevIndex + 1;
|
|
||||||
setColor(colors[newIndex]); // Use the new index here
|
|
||||||
return newIndex;
|
|
||||||
});
|
|
||||||
}, 7500);
|
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const [currentIndex, setCurrentIndex] = useState(0);
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
|
|
||||||
// Auto-cycle through measurements every 5 seconds
|
// Auto-cycle through measurements every 5 seconds
|
||||||
|
|
@ -147,7 +132,7 @@ export default function StatusSen5x(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip title={tooltip()}>
|
<Tooltip title={tooltip()}>
|
||||||
<PulseBox color={color} className={classes.box} >
|
<PulseBox color={colors[0] ?? ""} colors={colors} className={classes.box} >
|
||||||
<Grid2 container direction="column" >
|
<Grid2 container direction="column" >
|
||||||
<Grid2 container direction="column"
|
<Grid2 container direction="column"
|
||||||
className={`${classes.carouselItem} ${
|
className={`${classes.carouselItem} ${
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue