Made animations with X and Check
This commit is contained in:
parent
82f29cfcb6
commit
efd4f80dcd
5 changed files with 169 additions and 71 deletions
39
src/frontend/package-lock.json
generated
39
src/frontend/package-lock.json
generated
|
|
@ -44,6 +44,7 @@
|
|||
"clsx": "^1.2.1",
|
||||
"dompurify": "^3.0.5",
|
||||
"esbuild": "^0.17.19",
|
||||
"framer-motion": "^11.0.6",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-react": "^0.331.0",
|
||||
"moment": "^2.29.4",
|
||||
|
|
@ -5918,6 +5919,44 @@
|
|||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "11.0.6",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.0.6.tgz",
|
||||
"integrity": "sha512-BpO3mWF8UwxzO3Ca5AmSkrg14QYTeJa9vKgoLOoBdBdTPj0e81i1dMwnX6EQJXRieUx20uiDBXq8bA6y7N6b8Q==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@emotion/is-prop-valid": "^0.8.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion/node_modules/@emotion/is-prop-valid": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
|
||||
"integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emotion/memoize": "0.7.4"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion/node_modules/@emotion/memoize": {
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
|
||||
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "11.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
"clsx": "^1.2.1",
|
||||
"dompurify": "^3.0.5",
|
||||
"esbuild": "^0.17.19",
|
||||
"framer-motion": "^11.0.6",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-react": "^0.331.0",
|
||||
"moment": "^2.29.4",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import ShadTooltip from "../../components/ShadTooltipComponent";
|
|||
import IconComponent from "../../components/genericIconComponent";
|
||||
import InputComponent from "../../components/inputComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import Checkmark from "../../components/ui/checkmark";
|
||||
import Loading from "../../components/ui/loading";
|
||||
import { Textarea } from "../../components/ui/textarea";
|
||||
import { priorityFields } from "../../constants/constants";
|
||||
|
|
@ -19,6 +20,7 @@ import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils";
|
|||
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
|
||||
import { classNames, cn, getFieldTitle } from "../../utils/utils";
|
||||
import ParameterComponent from "./components/parameterComponent";
|
||||
import Xmark from "../../components/ui/xmark";
|
||||
|
||||
export default function GenericNode({
|
||||
data,
|
||||
|
|
@ -118,7 +120,7 @@ export default function GenericNode({
|
|||
} else {
|
||||
setValidationStatus(null);
|
||||
}
|
||||
}, [flowPool, data.id]);
|
||||
}, [flowPool[data.id], data.id]);
|
||||
|
||||
const showNode = data.showNode ?? true;
|
||||
|
||||
|
|
@ -156,52 +158,44 @@ export default function GenericNode({
|
|||
);
|
||||
};
|
||||
|
||||
const getIconPlayOrPauseComponent = (name, className) => (
|
||||
<IconComponent
|
||||
name={name}
|
||||
className={`h-4 fill-current stroke-2 ${className}`}
|
||||
/>
|
||||
);
|
||||
|
||||
const getStatusClassName = (
|
||||
buildStatus: BuildStatus | undefined,
|
||||
validationStatus: validationStatusType | null
|
||||
) => {
|
||||
console.log(buildStatus);
|
||||
const isValid = validationStatus && validationStatus.valid;
|
||||
if (isValid) {
|
||||
return "green-status";
|
||||
} else if (!isValid && buildStatus === BuildStatus.INACTIVE) {
|
||||
return "gray-status";
|
||||
} else if (!validationStatus && buildStatus === BuildStatus.BUILT) {
|
||||
return "green-status";
|
||||
} else if (!isValid && buildStatus === BuildStatus.BUILT) {
|
||||
return "red-status";
|
||||
} else if (!validationStatus && buildStatus === BuildStatus.TO_BUILD) {
|
||||
return "green-status";
|
||||
} else {
|
||||
return "yellow-status";
|
||||
}
|
||||
};
|
||||
const isDark = useDarkStore((state) => state.dark);
|
||||
const renderIconStatusComponents = (
|
||||
const renderIconStatus = (
|
||||
buildStatus: BuildStatus | undefined,
|
||||
validationStatus: validationStatusType | null
|
||||
) => {
|
||||
const className = getStatusClassName(buildStatus, validationStatus);
|
||||
return <>{getIconPlayOrPauseComponent("CircleDot", className)}</>;
|
||||
};
|
||||
const renderIconPlayOrPauseComponents = (
|
||||
buildStatus: BuildStatus | undefined,
|
||||
) => {
|
||||
if (buildStatus === BuildStatus.BUILDING) {
|
||||
return <Loading />;
|
||||
return <Loading className="text-medium-indigo" />;
|
||||
} else {
|
||||
return (
|
||||
<IconComponent
|
||||
name="Play"
|
||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-muted-foreground group-hover:text-medium-indigo"
|
||||
/>
|
||||
<>
|
||||
<IconComponent
|
||||
name="Play"
|
||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-medium-indigo opacity-0 transition-all group-hover:opacity-100"
|
||||
/>
|
||||
{validationStatus && validationStatus.valid ? (
|
||||
<Checkmark
|
||||
className="absolute ml-0.5 h-5 stroke-2 text-status-green opacity-100 transition-all group-hover:opacity-0"
|
||||
isVisible={true}
|
||||
/>
|
||||
) : validationStatus &&
|
||||
!validationStatus.valid &&
|
||||
buildStatus === BuildStatus.INACTIVE ? (
|
||||
<IconComponent
|
||||
name="Play"
|
||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-green opacity-30 transition-all group-hover:opacity-0"
|
||||
/>
|
||||
) : validationStatus && !validationStatus.valid ? (
|
||||
<Xmark
|
||||
isVisible={true}
|
||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-red opacity-100 transition-all group-hover:opacity-0"
|
||||
/>
|
||||
) : (
|
||||
<IconComponent
|
||||
name="Play"
|
||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-muted-foreground opacity-100 transition-all group-hover:opacity-0"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
@ -467,53 +461,40 @@ export default function GenericNode({
|
|||
variant="secondary"
|
||||
className={"group h-9 px-1.5"}
|
||||
onClick={() => {
|
||||
if (data?.buildStatus === BuildStatus.BUILDING || isBuilding)
|
||||
if (buildStatus === BuildStatus.BUILDING || isBuilding)
|
||||
return;
|
||||
setValidationStatus(null);
|
||||
buildFlow(data.id);
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<ShadTooltip
|
||||
content={
|
||||
"Build"
|
||||
buildStatus === BuildStatus.BUILDING ? (
|
||||
<span>Building...</span>
|
||||
) : !validationStatus ? (
|
||||
<span className="flex">Build to validate status.</span>
|
||||
) : (
|
||||
<div className="max-h-96 overflow-auto">
|
||||
{typeof validationStatus.params === "string"
|
||||
? `${durationString}\n${validationStatus.params}`
|
||||
.split("\n")
|
||||
.map((line, index) => (
|
||||
<div key={index}>{line}</div>
|
||||
))
|
||||
: durationString}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
side="bottom"
|
||||
>
|
||||
<div className="generic-node-status-position flex items-center justify-center">
|
||||
{renderIconPlayOrPauseComponents(
|
||||
buildStatus,
|
||||
)}
|
||||
{renderIconStatus(buildStatus, validationStatus)}
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
</Button>
|
||||
)}
|
||||
<div className="">
|
||||
<ShadTooltip
|
||||
content={
|
||||
data?.buildStatus === BuildStatus.BUILDING ? (
|
||||
<span>Building...</span>
|
||||
) : !validationStatus ? (
|
||||
<span className="flex">Build to validate status.</span>
|
||||
) : (
|
||||
<div className="max-h-96 overflow-auto">
|
||||
{typeof validationStatus.params === "string"
|
||||
? `${durationString}\n${validationStatus.params}`
|
||||
.split("\n")
|
||||
.map((line, index) => <div key={index}>{line}</div>)
|
||||
: durationString}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
>
|
||||
<div>
|
||||
{renderIconStatusComponents(
|
||||
data?.buildStatus,
|
||||
validationStatus
|
||||
)}
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
31
src/frontend/src/components/ui/checkmark.tsx
Normal file
31
src/frontend/src/components/ui/checkmark.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { AnimatePresence, motion } from "framer-motion";
|
||||
export default function Checkmark({ initial = true, isVisible, className }) {
|
||||
return (
|
||||
<AnimatePresence initial={initial}>
|
||||
{isVisible && (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className={"CheckIcon " + className}
|
||||
>
|
||||
<motion.path
|
||||
initial={{ pathLength: 0, pathOffset: 1 }}
|
||||
animate={{ pathLength: 1, pathOffset: 0 }}
|
||||
exit={{ pathLength: 0, pathOffset: 1 }}
|
||||
transition={{
|
||||
type: "tween",
|
||||
duration: 0.3,
|
||||
ease: isVisible ? "easeOut" : "easeIn",
|
||||
}}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M20 6 9 17l-5-5"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
46
src/frontend/src/components/ui/xmark.tsx
Normal file
46
src/frontend/src/components/ui/xmark.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { AnimatePresence, motion } from "framer-motion";
|
||||
export default function Xmark({ initial = true, isVisible, className }) {
|
||||
return (
|
||||
<AnimatePresence initial={initial}>
|
||||
{isVisible && (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className={"CheckIcon " + className}
|
||||
>
|
||||
<motion.path
|
||||
|
||||
initial={{ pathLength: 0, pathOffset: 1, strokeLinecap: "butt" }}
|
||||
animate={{ pathLength: 1, pathOffset: 0, strokeLinecap: "round" }}
|
||||
exit={{ pathLength: 0, pathOffset: 1, strokeLinecap: "butt" }}
|
||||
transition={{
|
||||
type: "tween",
|
||||
duration: 0.3,
|
||||
ease: isVisible ? "easeOut" : "easeIn",
|
||||
delay: 0.2,
|
||||
}}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M18 6 6 18"
|
||||
/>
|
||||
<motion.path
|
||||
initial={{ pathLength: 0 }}
|
||||
animate={{ pathLength: 1 }}
|
||||
exit={{ pathLength: 0 }}
|
||||
transition={{
|
||||
type: "tween",
|
||||
duration: 0.3,
|
||||
ease: isVisible ? "easeOut" : "easeIn",
|
||||
}}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="m6 6 12 12"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue