Adding Node Toolbar ReactFlow
This commit is contained in:
parent
ad9c2a270c
commit
8aa2d2e4c9
2 changed files with 300 additions and 186 deletions
|
|
@ -30,6 +30,9 @@ import { TabsContext } from "../../contexts/tabsContext";
|
||||||
import { debounce } from "../../utils";
|
import { debounce } from "../../utils";
|
||||||
import TooltipReact from "../../components/ReactTooltipComponent";
|
import TooltipReact from "../../components/ReactTooltipComponent";
|
||||||
import Tooltip from "../../components/TooltipComponent";
|
import Tooltip from "../../components/TooltipComponent";
|
||||||
|
import { NodeToolbar } from "reactflow";
|
||||||
|
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
|
||||||
|
|
||||||
export default function GenericNode({
|
export default function GenericNode({
|
||||||
data,
|
data,
|
||||||
selected,
|
selected,
|
||||||
|
|
@ -97,199 +100,173 @@ export default function GenericNode({
|
||||||
deleteNode(data.id);
|
deleteNode(data.id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<>
|
||||||
className={classNames(
|
<NodeToolbar>
|
||||||
selected ? "border border-blue-500" : "border dark:border-gray-700",
|
<NodeToolbarComponent
|
||||||
"prompt-node relative flex w-96 flex-col justify-center rounded-lg bg-white dark:bg-gray-900"
|
data={data}
|
||||||
)}
|
openPopUp={openPopUp}
|
||||||
>
|
deleteNode={deleteNode}
|
||||||
<div className="flex w-full items-center justify-between gap-8 rounded-t-lg border-b bg-gray-50 p-4 dark:border-b-gray-700 dark:bg-gray-800 dark:text-white ">
|
></NodeToolbarComponent>
|
||||||
<div className="flex w-full items-center gap-2 truncate text-lg">
|
</NodeToolbar>
|
||||||
<Icon
|
|
||||||
className="h-10 w-10 rounded p-1"
|
<div
|
||||||
style={{
|
className={classNames(
|
||||||
color: nodeColors[types[data.type]] ?? nodeColors.unknown,
|
selected ? "border border-blue-500" : "border dark:border-gray-700",
|
||||||
}}
|
"prompt-node relative flex w-96 flex-col justify-center rounded-lg bg-white dark:bg-gray-900"
|
||||||
/>
|
)}
|
||||||
<div className="ml-2 truncate">
|
>
|
||||||
<TooltipReact
|
<div className="flex w-full items-center justify-between gap-8 rounded-t-lg border-b bg-gray-50 p-4 dark:border-b-gray-700 dark:bg-gray-800 dark:text-white ">
|
||||||
delayShow={1000}
|
<div className="flex w-full items-center gap-2 truncate text-lg">
|
||||||
selector={`node-selector-${data.type}`}
|
<Icon
|
||||||
htmlContent={data.type}
|
className="h-10 w-10 rounded p-1"
|
||||||
position="top"
|
style={{
|
||||||
>
|
color: nodeColors[types[data.type]] ?? nodeColors.unknown,
|
||||||
<div className="ml-2 truncate">{data.type}</div>
|
}}
|
||||||
</TooltipReact>
|
/>
|
||||||
</div>
|
<div className="ml-2 truncate">
|
||||||
</div>
|
<TooltipReact
|
||||||
<div className="flex gap-3">
|
delayShow={1000}
|
||||||
<button
|
selector={`node-selector-${data.type}`}
|
||||||
className="relative"
|
htmlContent={data.type}
|
||||||
onClick={(event) => {
|
position="top"
|
||||||
event.preventDefault();
|
>
|
||||||
openPopUp(<NodeModal data={data} />);
|
<div className="ml-2 truncate">{data.type}</div>
|
||||||
}}
|
</TooltipReact>
|
||||||
>
|
|
||||||
<div className=" absolute -right-1 -top-2 text-red-600">
|
|
||||||
{Object.keys(data.node.template).some(
|
|
||||||
(t) =>
|
|
||||||
data.node.template[t].advanced &&
|
|
||||||
data.node.template[t].required
|
|
||||||
)
|
|
||||||
? " *"
|
|
||||||
: ""}
|
|
||||||
</div>
|
</div>
|
||||||
<Cog6ToothIcon
|
</div>
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<div>
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
!validationStatus ? (
|
||||||
|
"Validating..."
|
||||||
|
) : (
|
||||||
|
<div className="max-h-96 overflow-auto">
|
||||||
|
{validationStatus.params
|
||||||
|
.split("\n")
|
||||||
|
.map((line, index) => (
|
||||||
|
<div key={index}>{line}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="w-5 h-5 relative top-[3px]">
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
validationStatus && validationStatus.valid
|
||||||
|
? "w-4 h-4 rounded-full bg-green-500 opacity-100"
|
||||||
|
: "w-4 h-4 rounded-full bg-gray-500 opacity-0 hidden animate-spin",
|
||||||
|
"absolute w-4 hover:text-gray-500 hover:dark:text-gray-300 transition-all ease-in-out duration-200"
|
||||||
|
)}
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
validationStatus && !validationStatus.valid
|
||||||
|
? "w-4 h-4 rounded-full bg-red-500 opacity-100"
|
||||||
|
: "w-4 h-4 rounded-full bg-gray-500 opacity-0 hidden animate-spin",
|
||||||
|
"absolute w-4 hover:text-gray-500 hover:dark:text-gray-300 transition-all ease-in-out duration-200"
|
||||||
|
)}
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
!validationStatus
|
||||||
|
? "w-4 h-4 rounded-full bg-yellow-500 opacity-100"
|
||||||
|
: "w-4 h-4 rounded-full bg-gray-500 opacity-0 hidden animate-spin",
|
||||||
|
"absolute w-4 hover:text-gray-500 hover:dark:text-gray-300 transition-all ease-in-out duration-200"
|
||||||
|
)}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="h-full w-full py-5">
|
||||||
|
<div className="w-full px-5 pb-3 text-sm text-gray-500 dark:text-gray-300">
|
||||||
|
{data.node.description}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<>
|
||||||
|
{Object.keys(data.node.template)
|
||||||
|
.filter((t) => t.charAt(0) !== "_")
|
||||||
|
.map((t: string, idx) => (
|
||||||
|
<div key={idx}>
|
||||||
|
{/* {idx === 0 ? (
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
"px-5 py-2 mt-2 dark:text-white text-center",
|
||||||
|
Object.keys(data.node.template).filter(
|
||||||
|
(key) =>
|
||||||
|
!key.startsWith("_") &&
|
||||||
|
data.node.template[key].show &&
|
||||||
|
!data.node.template[key].advanced
|
||||||
|
).length === 0
|
||||||
|
? "hidden"
|
||||||
|
: ""
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Inputs
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)} */}
|
||||||
|
{data.node.template[t].show &&
|
||||||
|
!data.node.template[t].advanced ? (
|
||||||
|
<ParameterComponent
|
||||||
|
data={data}
|
||||||
|
color={
|
||||||
|
nodeColors[types[data.node.template[t].type]] ??
|
||||||
|
nodeColors.unknown
|
||||||
|
}
|
||||||
|
title={
|
||||||
|
data.node.template[t].display_name
|
||||||
|
? data.node.template[t].display_name
|
||||||
|
: data.node.template[t].name
|
||||||
|
? toTitleCase(data.node.template[t].name)
|
||||||
|
: toTitleCase(t)
|
||||||
|
}
|
||||||
|
name={t}
|
||||||
|
tooltipTitle={
|
||||||
|
"Type: " +
|
||||||
|
data.node.template[t].type +
|
||||||
|
(data.node.template[t].list ? " list" : "")
|
||||||
|
}
|
||||||
|
required={data.node.template[t].required}
|
||||||
|
id={data.node.template[t].type + "|" + t + "|" + data.id}
|
||||||
|
left={true}
|
||||||
|
type={data.node.template[t].type}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
Object.keys(data.node.template).some(
|
Object.keys(data.node.template).length < 1 ? "hidden" : "",
|
||||||
(t) =>
|
"flex w-full justify-center"
|
||||||
data.node.template[t].advanced && data.node.template[t].show
|
|
||||||
)
|
|
||||||
? ""
|
|
||||||
: "hidden",
|
|
||||||
"w-5 h-5 dark:text-gray-300"
|
|
||||||
)}
|
)}
|
||||||
></Cog6ToothIcon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
deleteNode(data.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TrashIcon className="w-5 h-5 dark:text-gray-300"></TrashIcon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Tooltip
|
|
||||||
title={
|
|
||||||
!validationStatus ? (
|
|
||||||
"Validating..."
|
|
||||||
) : (
|
|
||||||
<div className="max-h-96 overflow-auto">
|
|
||||||
{validationStatus.params.split("\n").map((line, index) => (
|
|
||||||
<div key={index}>{line}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<div className="w-5 h-5 relative top-[3px]">
|
{" "}
|
||||||
<div
|
</div>
|
||||||
className={classNames(
|
{/* <div className="px-5 py-2 mt-2 dark:text-white text-center">
|
||||||
validationStatus && validationStatus.valid
|
Output
|
||||||
? "w-4 h-4 rounded-full bg-green-500 opacity-100"
|
</div> */}
|
||||||
: "w-4 h-4 rounded-full bg-gray-500 opacity-0 hidden animate-spin",
|
<ParameterComponent
|
||||||
"absolute w-4 hover:text-gray-500 hover:dark:text-gray-300 transition-all ease-in-out duration-200"
|
data={data}
|
||||||
)}
|
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
|
||||||
></div>
|
title={data.type}
|
||||||
<div
|
tooltipTitle={`Type: ${data.node.base_classes.join(" | ")}`}
|
||||||
className={classNames(
|
id={[data.type, data.id, ...data.node.base_classes].join("|")}
|
||||||
validationStatus && !validationStatus.valid
|
type={data.node.base_classes.join("|")}
|
||||||
? "w-4 h-4 rounded-full bg-red-500 opacity-100"
|
left={false}
|
||||||
: "w-4 h-4 rounded-full bg-gray-500 opacity-0 hidden animate-spin",
|
/>
|
||||||
"absolute w-4 hover:text-gray-500 hover:dark:text-gray-300 transition-all ease-in-out duration-200"
|
</>
|
||||||
)}
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
!validationStatus
|
|
||||||
? "w-4 h-4 rounded-full bg-yellow-500 opacity-100"
|
|
||||||
: "w-4 h-4 rounded-full bg-gray-500 opacity-0 hidden animate-spin",
|
|
||||||
"absolute w-4 hover:text-gray-500 hover:dark:text-gray-300 transition-all ease-in-out duration-200"
|
|
||||||
)}
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
<div className="h-full w-full py-5">
|
|
||||||
<div className="w-full px-5 pb-3 text-sm text-gray-500 dark:text-gray-300">
|
|
||||||
{data.node.description}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<>
|
|
||||||
{Object.keys(data.node.template)
|
|
||||||
.filter((t) => t.charAt(0) !== "_")
|
|
||||||
.map((t: string, idx) => (
|
|
||||||
<div key={idx}>
|
|
||||||
{/* {idx === 0 ? (
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
"px-5 py-2 mt-2 dark:text-white text-center",
|
|
||||||
Object.keys(data.node.template).filter(
|
|
||||||
(key) =>
|
|
||||||
!key.startsWith("_") &&
|
|
||||||
data.node.template[key].show &&
|
|
||||||
!data.node.template[key].advanced
|
|
||||||
).length === 0
|
|
||||||
? "hidden"
|
|
||||||
: ""
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
Inputs
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)} */}
|
|
||||||
{data.node.template[t].show &&
|
|
||||||
!data.node.template[t].advanced ? (
|
|
||||||
<ParameterComponent
|
|
||||||
data={data}
|
|
||||||
color={
|
|
||||||
nodeColors[types[data.node.template[t].type]] ??
|
|
||||||
nodeColors.unknown
|
|
||||||
}
|
|
||||||
title={
|
|
||||||
data.node.template[t].display_name
|
|
||||||
? data.node.template[t].display_name
|
|
||||||
: data.node.template[t].name
|
|
||||||
? toTitleCase(data.node.template[t].name)
|
|
||||||
: toTitleCase(t)
|
|
||||||
}
|
|
||||||
name={t}
|
|
||||||
tooltipTitle={
|
|
||||||
"Type: " +
|
|
||||||
data.node.template[t].type +
|
|
||||||
(data.node.template[t].list ? " list" : "")
|
|
||||||
}
|
|
||||||
required={data.node.template[t].required}
|
|
||||||
id={data.node.template[t].type + "|" + t + "|" + data.id}
|
|
||||||
left={true}
|
|
||||||
type={data.node.template[t].type}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
Object.keys(data.node.template).length < 1 ? "hidden" : "",
|
|
||||||
"flex w-full justify-center"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{" "}
|
|
||||||
</div>
|
|
||||||
{/* <div className="px-5 py-2 mt-2 dark:text-white text-center">
|
|
||||||
Output
|
|
||||||
</div> */}
|
|
||||||
<ParameterComponent
|
|
||||||
data={data}
|
|
||||||
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
|
|
||||||
title={data.type}
|
|
||||||
tooltipTitle={`Type: ${data.node.base_classes.join(" | ")}`}
|
|
||||||
id={[data.type, data.id, ...data.node.base_classes].join("|")}
|
|
||||||
type={data.node.base_classes.join("|")}
|
|
||||||
left={false}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Menu, Transition } from "@headlessui/react";
|
||||||
|
import { EllipsisVerticalIcon } from "@heroicons/react/20/solid";
|
||||||
|
import {
|
||||||
|
Cog6ToothIcon,
|
||||||
|
TrashIcon,
|
||||||
|
PencilSquareIcon,
|
||||||
|
DocumentDuplicateIcon,
|
||||||
|
} from "@heroicons/react/24/outline";
|
||||||
|
import { classNames } from "../../../../utils";
|
||||||
|
import { Fragment } from "react";
|
||||||
|
import NodeModal from "../../../../modals/NodeModal";
|
||||||
|
|
||||||
|
const NodeToolbarComponent = (props) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="h-10 w-26">
|
||||||
|
<span className="isolate inline-flex rounded-md shadow-sm">
|
||||||
|
<button
|
||||||
|
className="hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative inline-flex items-center rounded-l-md bg-white px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
|
||||||
|
onClick={() => {
|
||||||
|
props.deleteNode(props.data.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TrashIcon className="w-5 h-5 dark:text-gray-300"></TrashIcon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className={classNames(
|
||||||
|
Object.keys(props.data.node.template).some(
|
||||||
|
(t) =>
|
||||||
|
props.data.node.template[t].advanced &&
|
||||||
|
props.data.node.template[t].show
|
||||||
|
)
|
||||||
|
? "hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
|
||||||
|
: "hidden"
|
||||||
|
)}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
props.openPopUp(<NodeModal data={props.data} />);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className=" absolute -right-1 -top-2 text-red-600">
|
||||||
|
{Object.keys(props.data.node.template).some(
|
||||||
|
(t) =>
|
||||||
|
props.data.node.template[t].advanced &&
|
||||||
|
props.data.node.template[t].required
|
||||||
|
)
|
||||||
|
? " *"
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
<Cog6ToothIcon
|
||||||
|
className={classNames(
|
||||||
|
Object.keys(props.data.node.template).some(
|
||||||
|
(t) =>
|
||||||
|
props.data.node.template[t].advanced &&
|
||||||
|
props.data.node.template[t].show
|
||||||
|
)
|
||||||
|
? ""
|
||||||
|
: "hidden",
|
||||||
|
"w-5 h-5 dark:text-gray-300"
|
||||||
|
)}
|
||||||
|
></Cog6ToothIcon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Menu as="div" className="relative inline-block text-left z-100">
|
||||||
|
<button className="hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10 rounded-r-md">
|
||||||
|
<div>
|
||||||
|
<Menu.Button className="flex items-center">
|
||||||
|
<EllipsisVerticalIcon
|
||||||
|
className="w-5 h-5 dark:text-gray-300"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</Menu.Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Transition
|
||||||
|
as={Fragment}
|
||||||
|
enter="transition ease-out duration-100"
|
||||||
|
enterFrom="transform opacity-0 scale-95"
|
||||||
|
enterTo="transform opacity-100 scale-100"
|
||||||
|
leave="transition ease-in duration-75"
|
||||||
|
leaveFrom="transform opacity-100 scale-100"
|
||||||
|
leaveTo="transform opacity-0 scale-95"
|
||||||
|
>
|
||||||
|
<Menu.Items className="absolute z-40 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none top-[28px]">
|
||||||
|
<div className="py-1">
|
||||||
|
<Menu.Item>
|
||||||
|
{({ active }) => (
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className={classNames(
|
||||||
|
active
|
||||||
|
? "bg-gray-100 text-gray-900"
|
||||||
|
: "text-gray-700",
|
||||||
|
"group flex items-center px-4 py-2 text-sm"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<PencilSquareIcon
|
||||||
|
className="mr-3 h-5 w-5 text-gray-400 group-hover:text-gray-500"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item>
|
||||||
|
{({ active }) => (
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className={classNames(
|
||||||
|
active
|
||||||
|
? "bg-gray-100 text-gray-900"
|
||||||
|
: "text-gray-700",
|
||||||
|
"group flex items-center px-4 py-2 text-sm"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<DocumentDuplicateIcon
|
||||||
|
className="mr-3 h-5 w-5 text-gray-400 group-hover:text-gray-500"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
Duplicate
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</Menu.Item>
|
||||||
|
</div>
|
||||||
|
</Menu.Items>
|
||||||
|
</Transition>
|
||||||
|
</button>
|
||||||
|
</Menu>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NodeToolbarComponent;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue