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",
|
"clsx": "^1.2.1",
|
||||||
"dompurify": "^3.0.5",
|
"dompurify": "^3.0.5",
|
||||||
"esbuild": "^0.17.19",
|
"esbuild": "^0.17.19",
|
||||||
|
"framer-motion": "^11.0.6",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.331.0",
|
"lucide-react": "^0.331.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
|
@ -5918,6 +5919,44 @@
|
||||||
"url": "https://github.com/sponsors/rawify"
|
"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": {
|
"node_modules/fs-extra": {
|
||||||
"version": "11.2.0",
|
"version": "11.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"dompurify": "^3.0.5",
|
"dompurify": "^3.0.5",
|
||||||
"esbuild": "^0.17.19",
|
"esbuild": "^0.17.19",
|
||||||
|
"framer-motion": "^11.0.6",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.331.0",
|
"lucide-react": "^0.331.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
import InputComponent from "../../components/inputComponent";
|
import InputComponent from "../../components/inputComponent";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
|
import Checkmark from "../../components/ui/checkmark";
|
||||||
import Loading from "../../components/ui/loading";
|
import Loading from "../../components/ui/loading";
|
||||||
import { Textarea } from "../../components/ui/textarea";
|
import { Textarea } from "../../components/ui/textarea";
|
||||||
import { priorityFields } from "../../constants/constants";
|
import { priorityFields } from "../../constants/constants";
|
||||||
|
|
@ -19,6 +20,7 @@ import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils";
|
||||||
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
|
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
|
||||||
import { classNames, cn, getFieldTitle } from "../../utils/utils";
|
import { classNames, cn, getFieldTitle } from "../../utils/utils";
|
||||||
import ParameterComponent from "./components/parameterComponent";
|
import ParameterComponent from "./components/parameterComponent";
|
||||||
|
import Xmark from "../../components/ui/xmark";
|
||||||
|
|
||||||
export default function GenericNode({
|
export default function GenericNode({
|
||||||
data,
|
data,
|
||||||
|
|
@ -118,7 +120,7 @@ export default function GenericNode({
|
||||||
} else {
|
} else {
|
||||||
setValidationStatus(null);
|
setValidationStatus(null);
|
||||||
}
|
}
|
||||||
}, [flowPool, data.id]);
|
}, [flowPool[data.id], data.id]);
|
||||||
|
|
||||||
const showNode = data.showNode ?? true;
|
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 isDark = useDarkStore((state) => state.dark);
|
||||||
const renderIconStatusComponents = (
|
const renderIconStatus = (
|
||||||
buildStatus: BuildStatus | undefined,
|
buildStatus: BuildStatus | undefined,
|
||||||
validationStatus: validationStatusType | null
|
validationStatus: validationStatusType | null
|
||||||
) => {
|
|
||||||
const className = getStatusClassName(buildStatus, validationStatus);
|
|
||||||
return <>{getIconPlayOrPauseComponent("CircleDot", className)}</>;
|
|
||||||
};
|
|
||||||
const renderIconPlayOrPauseComponents = (
|
|
||||||
buildStatus: BuildStatus | undefined,
|
|
||||||
) => {
|
) => {
|
||||||
if (buildStatus === BuildStatus.BUILDING) {
|
if (buildStatus === BuildStatus.BUILDING) {
|
||||||
return <Loading />;
|
return <Loading className="text-medium-indigo" />;
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<IconComponent
|
<>
|
||||||
name="Play"
|
<IconComponent
|
||||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-muted-foreground group-hover:text-medium-indigo"
|
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"
|
variant="secondary"
|
||||||
className={"group h-9 px-1.5"}
|
className={"group h-9 px-1.5"}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (data?.buildStatus === BuildStatus.BUILDING || isBuilding)
|
if (buildStatus === BuildStatus.BUILDING || isBuilding)
|
||||||
return;
|
return;
|
||||||
|
setValidationStatus(null);
|
||||||
buildFlow(data.id);
|
buildFlow(data.id);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<ShadTooltip
|
<ShadTooltip
|
||||||
content={
|
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"
|
side="bottom"
|
||||||
>
|
>
|
||||||
<div className="generic-node-status-position flex items-center justify-center">
|
<div className="generic-node-status-position flex items-center justify-center">
|
||||||
{renderIconPlayOrPauseComponents(
|
{renderIconStatus(buildStatus, validationStatus)}
|
||||||
buildStatus,
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</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>
|
||||||
</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