animate chips with react-transition-group

This commit is contained in:
Josh Hawkins 2024-02-09 08:19:12 -06:00
parent 5bb382703a
commit 4e28bc8d50
5 changed files with 97 additions and 53 deletions

38
web/package-lock.json generated
View File

@ -42,6 +42,7 @@
"react-hook-form": "^7.48.2",
"react-icons": "^4.12.0",
"react-router-dom": "^6.20.1",
"react-transition-group": "^4.4.5",
"react-use-websocket": "^4.5.0",
"recoil": "^0.7.7",
"sonner": "^1.4.0",
@ -64,6 +65,7 @@
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"@types/react-icons": "^3.0.0",
"@types/react-transition-group": "^4.4.10",
"@types/strftime": "^0.9.8",
"@typescript-eslint/eslint-plugin": "^6.10.0",
"@typescript-eslint/parser": "^6.10.0",
@ -2498,6 +2500,15 @@
"react-icons": "*"
}
},
"node_modules/@types/react-transition-group": {
"version": "4.4.10",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz",
"integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/scheduler": {
"version": "0.16.8",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
@ -3741,8 +3752,7 @@
"node_modules/csstype": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
"devOptional": true
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
},
"node_modules/data-urls": {
"version": "5.0.0",
@ -3992,6 +4002,15 @@
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
"dev": true
},
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"node_modules/dom-walk": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
@ -6867,6 +6886,21 @@
}
}
},
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
},
"peerDependencies": {
"react": ">=16.6.0",
"react-dom": ">=16.6.0"
}
},
"node_modules/react-use-websocket": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/react-use-websocket/-/react-use-websocket-4.5.0.tgz",

View File

@ -47,6 +47,7 @@
"react-hook-form": "^7.48.2",
"react-icons": "^4.12.0",
"react-router-dom": "^6.20.1",
"react-transition-group": "^4.4.5",
"react-use-websocket": "^4.5.0",
"recoil": "^0.7.7",
"sonner": "^1.4.0",
@ -69,6 +70,7 @@
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"@types/react-icons": "^3.0.0",
"@types/react-transition-group": "^4.4.10",
"@types/strftime": "^0.9.8",
"@typescript-eslint/eslint-plugin": "^6.10.0",
"@typescript-eslint/parser": "^6.10.0",

View File

@ -1,13 +1,38 @@
import { ReactNode } from "react";
import { ReactNode, useRef } from "react";
import { CSSTransition } from "react-transition-group";
type ChipProps = {
className?: string;
children?: ReactNode[];
in?: boolean;
};
export default function Chip({ className, children }: ChipProps) {
export default function Chip({
className,
children,
in: inProp = true,
}: ChipProps) {
const nodeRef = useRef(null);
return (
<div className={`flex px-2 py-1.5 rounded-2xl items-center ${className}`}>
<CSSTransition
in={inProp}
nodeRef={nodeRef}
timeout={500}
classNames={{
enter: "opacity-0",
enterActive: "opacity-100 transition-opacity duration-500 ease-in-out",
exit: "opacity-100",
exitActive: "opacity-0 transition-opacity duration-500 ease-in-out",
}}
unmountOnExit
>
<div
ref={nodeRef}
className={`flex px-2 py-1.5 rounded-2xl items-center ${className}`}
>
{children}
</div>
</CSSTransition>
);
}

View File

@ -24,7 +24,6 @@ type LivePlayerProps = {
className?: string;
cameraConfig: CameraConfig;
liveMode?: LivePlayerMode;
liveChips?: boolean;
showStillWithoutActivity?: boolean;
};
@ -34,7 +33,6 @@ export default function LivePlayer({
className,
cameraConfig,
liveMode = "mse",
liveChips = false,
showStillWithoutActivity = true,
}: LivePlayerProps) {
// camera activity
@ -64,7 +62,7 @@ export default function LivePlayer({
const newOptions = { ...options, [id]: value };
setOptions(newOptions);
},
[options, setOptions]
[options]
);
const searchParams = useMemo(
() =>
@ -79,7 +77,7 @@ export default function LivePlayer({
);
const handleToggleSettings = useCallback(() => {
setShowSettings(!showSettings);
}, [showSettings, setShowSettings]);
}, [showSettings]);
if (!cameraConfig) {
return <ActivityIndicator />;
@ -173,50 +171,36 @@ export default function LivePlayer({
</div>
)}
{liveChips && (
<div className="absolute flex left-2 top-2 gap-2">
<Chip
className={`bg-gray-500 bg-gradient-to-br ${
activeMotion ? "visible opacity-100" : "invisible opacity-0"
}`}
in={activeMotion}
className={`bg-gradient-to-br from-gray-400 to-gray-500 bg-gray-500/90`}
>
<MdLeakAdd
className={`w-4 h-4 ${
activeMotion ? "text-motion" : "text-white"
}`}
/>
<div className="ml-1 capitalize text-white text-xs">Motion</div>
<MdLeakAdd className="w-4 h-4 text-motion" />
<div className="ml-1 text-white text-xs">Motion</div>
</Chip>
{cameraConfig.audio.enabled_in_config && (
<Chip
className={`bg-gray-500 bg-gradient-to-br transition-all ${
activeAudio ? "visible opacity-100" : "invisible opacity-0"
}`}
in={activeAudio}
className={`bg-gradient-to-br from-gray-400 to-gray-500 bg-gray-500/90`}
>
<BsSoundwave
className={`w-4 h-4 ${
activeAudio ? "text-audio" : "text-white"
}`}
/>
<div className="ml-1 capitalize text-white text-xs">Sound</div>
<BsSoundwave className="w-4 h-4 text-audio" />
<div className="ml-1 text-white text-xs">Sound</div>
</Chip>
)}
<Chip
className={`bg-gray-500 bg-gradient-to-br transition-all ${
activeTracking ? "inline opacity-100" : "invisible opacity-0"
}`}
in={activeTracking}
className={`bg-gradient-to-br from-gray-400 to-gray-500 bg-gray-500/90 `}
>
<MdSelectAll
className={`w-4 h-4 ${
activeTracking ? "text-object" : "text-white"
}`}
/>
<div className="ml-1 capitalize text-white text-xs">Tracking</div>
<MdSelectAll className="w-4 h-4 text-object" />
<div className="ml-1 text-white text-xs">Tracking</div>
</Chip>
</div>
)}
<Chip className="absolute right-2 top-2 bg-gray-500 bg-gradient-to-br">
<MdCircle className="w-2 h-2 text-danger" />
<Chip className="absolute right-2 top-2 bg-gradient-to-br from-gray-300/50 to-gray-500/90">
<MdCircle className="w-2 h-2 drop-shadow-md shadow-danger text-danger" />
<div className="ml-1 capitalize text-white text-xs">
{cameraConfig.name.replaceAll("_", " ")}
</div>

View File

@ -87,7 +87,6 @@ function Live() {
key={camera.name}
className={`mb-2 md:mb-0 rounded-2xl bg-black ${grow}`}
cameraConfig={camera}
liveChips
/>
);
})}