refactor: handle rendering and filtering functionality (#3512)
This commit is contained in:
parent
2bd5d4b911
commit
2f1f1808b9
25 changed files with 761 additions and 322 deletions
|
|
@ -145,6 +145,10 @@ body {
|
|||
transition-duration: 150ms;
|
||||
}
|
||||
|
||||
.react-flow__edge.running .react-flow__edge-path {
|
||||
stroke: var(--status-blue) !important;
|
||||
}
|
||||
|
||||
.ag-react-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
|
|||
33
src/frontend/src/CustomEdges/index.tsx
Normal file
33
src/frontend/src/CustomEdges/index.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import useFlowStore from "@/stores/flowStore";
|
||||
import { BaseEdge, EdgeProps, getBezierPath, Position } from "reactflow";
|
||||
|
||||
export function DefaultEdge({
|
||||
sourceHandleId,
|
||||
source,
|
||||
sourceX,
|
||||
sourceY,
|
||||
target,
|
||||
targetHandleId,
|
||||
targetX,
|
||||
targetY,
|
||||
...props
|
||||
}: EdgeProps) {
|
||||
const getNode = useFlowStore((state) => state.getNode);
|
||||
|
||||
const sourceNode = getNode(source);
|
||||
const targetNode = getNode(target);
|
||||
|
||||
const sourceXNew = (sourceNode?.position.x ?? 0) + (sourceNode?.width ?? 0);
|
||||
const targetXNew = targetNode?.position.x ?? 0;
|
||||
|
||||
const [edgePath] = getBezierPath({
|
||||
sourceX: sourceXNew,
|
||||
sourceY,
|
||||
sourcePosition: Position.Right,
|
||||
targetPosition: Position.Left,
|
||||
targetX: targetXNew,
|
||||
targetY,
|
||||
});
|
||||
|
||||
return <BaseEdge path={edgePath} {...props} />;
|
||||
}
|
||||
|
|
@ -1,30 +1,65 @@
|
|||
import { TOOLTIP_EMPTY } from "../../../../constants/constants";
|
||||
import useFlowStore from "../../../../stores/flowStore";
|
||||
import { useTypesStore } from "../../../../stores/typesStore";
|
||||
import { NodeType } from "../../../../types/flow";
|
||||
import { groupByFamily } from "../../../../utils/utils";
|
||||
import TooltipRenderComponent from "../tooltipRenderComponent";
|
||||
import { convertTestName } from "@/components/storeCardComponent/utils/convert-test-name";
|
||||
|
||||
export default function HandleTooltips({
|
||||
left,
|
||||
export default function HandleTooltipComponent({
|
||||
isInput,
|
||||
tooltipTitle,
|
||||
colors,
|
||||
isConnecting,
|
||||
isCompatible,
|
||||
isSameNode,
|
||||
}: {
|
||||
left: boolean;
|
||||
nodes: NodeType[];
|
||||
isInput: boolean;
|
||||
colors: string[];
|
||||
tooltipTitle: string;
|
||||
isConnecting: boolean;
|
||||
isCompatible: boolean;
|
||||
isSameNode: boolean;
|
||||
}) {
|
||||
const myData = useTypesStore((state) => state.data);
|
||||
const nodes = useFlowStore((state) => state.nodes);
|
||||
|
||||
let groupedObj: any = groupByFamily(myData, tooltipTitle!, left, nodes!);
|
||||
|
||||
if (groupedObj && groupedObj.length > 0) {
|
||||
//@ts-ignore
|
||||
return groupedObj.map((item, index) => {
|
||||
return <TooltipRenderComponent index={index} item={item} left={left} />;
|
||||
});
|
||||
} else {
|
||||
//@ts-ignore
|
||||
return <span data-testid={`empty-tooltip-filter`}>{TOOLTIP_EMPTY}</span>;
|
||||
}
|
||||
const tooltips = tooltipTitle.split("\n");
|
||||
const plural = tooltips.length > 1 ? "s" : "";
|
||||
return (
|
||||
<div className="py-1.5 font-medium text-muted-foreground">
|
||||
{isSameNode ? (
|
||||
"Can't connect to the same node"
|
||||
) : (
|
||||
<div className="flex items-start gap-1.5">
|
||||
{isConnecting ? (
|
||||
isCompatible ? (
|
||||
<span>
|
||||
<span className="font-semibold text-foreground">Connect</span>{" "}
|
||||
to
|
||||
</span>
|
||||
) : (
|
||||
<span>Incompatible with</span>
|
||||
)
|
||||
) : (
|
||||
<span className="text-foreground">
|
||||
{isInput ? `Input${plural}` : `Output${plural}`}:{" "}
|
||||
</span>
|
||||
)}
|
||||
{tooltips.map((word, index) => (
|
||||
<div
|
||||
className="rounded-sm px-1.5 text-background"
|
||||
style={{ backgroundColor: colors[index] }}
|
||||
data-testid={`${isInput ? "input" : "output"}-tooltip-${convertTestName(word)}`}
|
||||
>
|
||||
{word}
|
||||
</div>
|
||||
))}
|
||||
{isConnecting && <span>{isInput ? `input` : `output`}</span>}
|
||||
</div>
|
||||
)}
|
||||
{!isConnecting && (
|
||||
<div className="mt-2 flex flex-col gap-0.5 text-xs">
|
||||
<div>
|
||||
<b>Drag</b> to connect compatible {!isInput ? "inputs" : "outputs"}
|
||||
</div>
|
||||
<div>
|
||||
<b>Select</b> to filter compatible {!isInput ? "inputs" : "outputs"}{" "}
|
||||
and components
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ export default function NodeInputField({
|
|||
setFilterEdge={setFilterEdge}
|
||||
showNode={showNode}
|
||||
testIdComplement={`${data?.type?.toLowerCase()}-${showNode ? "shownode" : "noshownode"}`}
|
||||
nodeId={data.id}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ export default function NodeOutputField({
|
|||
id={id}
|
||||
title={title}
|
||||
edges={edges}
|
||||
nodeId={data.id}
|
||||
myData={myData}
|
||||
colors={colors}
|
||||
setFilterEdge={setFilterEdge}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import { useDarkStore } from "@/stores/darkStore";
|
||||
import useFlowStore from "@/stores/flowStore";
|
||||
import { useMemo, useState } from "react";
|
||||
import { Handle, Position } from "reactflow";
|
||||
import ShadTooltip from "../../../../components/shadTooltipComponent";
|
||||
import {
|
||||
|
|
@ -5,7 +8,7 @@ import {
|
|||
scapedJSONStringfy,
|
||||
} from "../../../../utils/reactflowUtils";
|
||||
import { classNames, cn, groupByFamily } from "../../../../utils/utils";
|
||||
import HandleTooltips from "../HandleTooltipComponent";
|
||||
import HandleTooltipComponent from "../HandleTooltipComponent";
|
||||
|
||||
export default function HandleRenderComponent({
|
||||
left,
|
||||
|
|
@ -20,6 +23,7 @@ export default function HandleRenderComponent({
|
|||
setFilterEdge,
|
||||
showNode,
|
||||
testIdComplement,
|
||||
nodeId,
|
||||
}: {
|
||||
left: boolean;
|
||||
nodes: any;
|
||||
|
|
@ -33,17 +37,168 @@ export default function HandleRenderComponent({
|
|||
setFilterEdge: any;
|
||||
showNode: any;
|
||||
testIdComplement?: string;
|
||||
nodeId: string;
|
||||
}) {
|
||||
const setHandleDragging = useFlowStore((state) => state.setHandleDragging);
|
||||
const setFilterType = useFlowStore((state) => state.setFilterType);
|
||||
const handleDragging = useFlowStore((state) => state.handleDragging);
|
||||
const filterType = useFlowStore((state) => state.filterType);
|
||||
const dark = useDarkStore((state) => state.dark);
|
||||
|
||||
const onConnect = useFlowStore((state) => state.onConnect);
|
||||
|
||||
const handleMouseUp = () => {
|
||||
setHandleDragging(undefined);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
};
|
||||
|
||||
const myId = useMemo(
|
||||
() => scapedJSONStringfy(proxy ? { ...id, proxy } : id),
|
||||
[id, proxy],
|
||||
);
|
||||
|
||||
const getConnection = useMemo(
|
||||
() =>
|
||||
(semiConnection: {
|
||||
source: string | undefined;
|
||||
sourceHandle: string | undefined;
|
||||
target: string | undefined;
|
||||
targetHandle: string | undefined;
|
||||
}) => ({
|
||||
source: semiConnection.source ?? nodeId,
|
||||
sourceHandle: semiConnection.sourceHandle ?? myId,
|
||||
target: semiConnection.target ?? nodeId,
|
||||
targetHandle: semiConnection.targetHandle ?? myId,
|
||||
}),
|
||||
[nodeId, myId],
|
||||
);
|
||||
|
||||
const sameDraggingNode = useMemo(
|
||||
() => (!left ? handleDragging?.target : handleDragging?.source) === nodeId,
|
||||
[left, handleDragging, nodeId],
|
||||
);
|
||||
|
||||
const ownDraggingHandle = useMemo(
|
||||
() =>
|
||||
handleDragging &&
|
||||
(left ? handleDragging?.target : handleDragging?.source) &&
|
||||
(left ? handleDragging.targetHandle : handleDragging.sourceHandle) ===
|
||||
myId,
|
||||
[handleDragging, left, myId],
|
||||
);
|
||||
|
||||
const sameFilterNode = useMemo(
|
||||
() => (!left ? filterType?.target : filterType?.source) === nodeId,
|
||||
[left, filterType, nodeId],
|
||||
);
|
||||
|
||||
const ownFilterHandle = useMemo(
|
||||
() =>
|
||||
filterType &&
|
||||
(left ? filterType?.target : filterType?.source) === nodeId &&
|
||||
(left ? filterType.targetHandle : filterType.sourceHandle) === myId,
|
||||
[filterType, left, myId],
|
||||
);
|
||||
|
||||
const sameNode = useMemo(
|
||||
() => sameDraggingNode || sameFilterNode,
|
||||
[sameDraggingNode, sameFilterNode],
|
||||
);
|
||||
const ownHandle = useMemo(
|
||||
() => ownDraggingHandle || ownFilterHandle,
|
||||
[ownDraggingHandle, ownFilterHandle],
|
||||
);
|
||||
|
||||
const draggingOpenHandle = useMemo(
|
||||
() =>
|
||||
handleDragging &&
|
||||
(left ? handleDragging.source : handleDragging.target) &&
|
||||
!ownDraggingHandle
|
||||
? isValidConnection(getConnection(handleDragging), nodes, edges)
|
||||
: false,
|
||||
[handleDragging, left, ownDraggingHandle, getConnection, nodes, edges],
|
||||
);
|
||||
|
||||
const filterOpenHandle = useMemo(
|
||||
() =>
|
||||
filterType &&
|
||||
(left ? filterType.source : filterType.target) &&
|
||||
!ownFilterHandle
|
||||
? isValidConnection(getConnection(filterType), nodes, edges)
|
||||
: false,
|
||||
[filterType, left, ownFilterHandle, getConnection, nodes, edges],
|
||||
);
|
||||
|
||||
const openHandle = useMemo(
|
||||
() => filterOpenHandle || draggingOpenHandle,
|
||||
[filterOpenHandle, draggingOpenHandle],
|
||||
);
|
||||
const filterPresent = useMemo(
|
||||
() => handleDragging || filterType,
|
||||
[handleDragging, filterType],
|
||||
);
|
||||
|
||||
const currentFilter = useMemo(
|
||||
() =>
|
||||
left
|
||||
? {
|
||||
targetHandle: myId,
|
||||
target: nodeId,
|
||||
source: undefined,
|
||||
sourceHandle: undefined,
|
||||
type: tooltipTitle,
|
||||
color: colors[0],
|
||||
}
|
||||
: {
|
||||
sourceHandle: myId,
|
||||
source: nodeId,
|
||||
target: undefined,
|
||||
targetHandle: undefined,
|
||||
type: tooltipTitle,
|
||||
color: colors[0],
|
||||
},
|
||||
[left, myId, nodeId, tooltipTitle, colors],
|
||||
);
|
||||
|
||||
const handleColor = useMemo(
|
||||
() =>
|
||||
filterPresent && !(openHandle || ownHandle)
|
||||
? dark
|
||||
? "conic-gradient(#374151 0deg 360deg)"
|
||||
: "conic-gradient(#cbd5e1 0deg 360deg)"
|
||||
: "conic-gradient(" +
|
||||
colors
|
||||
.concat(colors[0])
|
||||
.map(
|
||||
(color, index) =>
|
||||
color +
|
||||
" " +
|
||||
((360 / colors.length) * index - 360 / (colors.length * 4)) +
|
||||
"deg " +
|
||||
((360 / colors.length) * index + 360 / (colors.length * 4)) +
|
||||
"deg",
|
||||
)
|
||||
.join(" ,") +
|
||||
")",
|
||||
[filterPresent, openHandle, ownHandle, dark, colors],
|
||||
);
|
||||
|
||||
const [openTooltip, setOpenTooltip] = useState(false);
|
||||
return (
|
||||
<div>
|
||||
<ShadTooltip
|
||||
open={openTooltip}
|
||||
setOpen={setOpenTooltip}
|
||||
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
|
||||
delayDuration={1000}
|
||||
content={
|
||||
<HandleTooltips
|
||||
left={left}
|
||||
nodes={nodes}
|
||||
tooltipTitle={tooltipTitle!}
|
||||
<HandleTooltipComponent
|
||||
isInput={left}
|
||||
colors={colors}
|
||||
tooltipTitle={tooltipTitle}
|
||||
isConnecting={!!filterPresent && !ownHandle}
|
||||
isCompatible={openHandle}
|
||||
isSameNode={sameNode && !ownHandle}
|
||||
/>
|
||||
}
|
||||
side={left ? "left" : "right"}
|
||||
|
|
@ -54,47 +209,73 @@ export default function HandleRenderComponent({
|
|||
}`}
|
||||
type={left ? "target" : "source"}
|
||||
position={left ? Position.Left : Position.Right}
|
||||
key={scapedJSONStringfy(proxy ? { ...id, proxy } : id)}
|
||||
id={scapedJSONStringfy(proxy ? { ...id, proxy } : id)}
|
||||
key={myId}
|
||||
id={myId}
|
||||
isValidConnection={(connection) =>
|
||||
isValidConnection(connection, nodes, edges)
|
||||
}
|
||||
className={classNames(
|
||||
left ? "-ml-0.5" : "-mr-0.5",
|
||||
"z-20 h-3 w-3 rounded-full border-none bg-background",
|
||||
`group/handle z-20 h-6 w-6 rounded-full border-none bg-transparent transition-all`,
|
||||
)}
|
||||
style={{
|
||||
background:
|
||||
"conic-gradient(" +
|
||||
colors
|
||||
.concat(colors[0])
|
||||
.map(
|
||||
(color, index) =>
|
||||
color +
|
||||
" " +
|
||||
((360 / colors.length) * index -
|
||||
360 / (colors.length * 4)) +
|
||||
"deg " +
|
||||
((360 / colors.length) * index +
|
||||
360 / (colors.length * 4)) +
|
||||
"deg",
|
||||
)
|
||||
.join(" ,") +
|
||||
")",
|
||||
WebkitMaskImage: "radial-gradient(transparent 40%, black 44%)",
|
||||
maskImage: "radial-gradient(transparent 40%, black 44%)",
|
||||
}}
|
||||
onClick={() => {
|
||||
setFilterEdge(groupByFamily(myData, tooltipTitle!, left, nodes!));
|
||||
setFilterType(currentFilter);
|
||||
if (filterOpenHandle && filterType) {
|
||||
onConnect(getConnection(filterType));
|
||||
setFilterType(undefined);
|
||||
setFilterEdge([]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
onMouseUp={() => {
|
||||
setOpenTooltip(false);
|
||||
}}
|
||||
onContextMenu={(event) => {
|
||||
event.preventDefault();
|
||||
}}
|
||||
onMouseDown={(event) => {
|
||||
if (event.button === 0) {
|
||||
setHandleDragging(currentFilter);
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"pointer-events-none absolute left-1/2 top-[50%] z-30 flex h-0 w-0 -translate-x-1/2 translate-y-[-50%] items-center justify-center rounded-full bg-background transition-all group-hover/handle:bg-transparent",
|
||||
filterPresent
|
||||
? openHandle || ownHandle
|
||||
? cn(
|
||||
"h-4 w-4",
|
||||
ownHandle ? "bg-transparent" : "bg-background",
|
||||
)
|
||||
: ""
|
||||
: "group-hover/node:h-4 group-hover/node:w-4",
|
||||
)}
|
||||
></div>
|
||||
<div
|
||||
className="pointer-events-none absolute left-1/2 top-[50%] z-10 flex h-3 w-3 -translate-x-1/2 translate-y-[-50%] items-center justify-center rounded-full opacity-50 transition-all"
|
||||
style={{
|
||||
background: handleColor,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
data-testid={`gradient-handle-${testIdComplement}-${title.toLowerCase()}-${
|
||||
!showNode ? (left ? "target" : "source") : left ? "left" : "right"
|
||||
}`}
|
||||
className={classNames(
|
||||
`pointer-events-none absolute left-1/2 top-[50%] z-10 flex -translate-x-1/2 translate-y-[-50%] items-center justify-center rounded-full transition-all`,
|
||||
filterPresent
|
||||
? openHandle || ownHandle
|
||||
? cn("h-5 w-5")
|
||||
: cn("h-1.5 w-1.5")
|
||||
: cn("h-1.5 w-1.5 group-hover/node:h-5 group-hover/node:w-5"),
|
||||
)}
|
||||
style={{
|
||||
background: handleColor,
|
||||
}}
|
||||
/>
|
||||
</Handle>
|
||||
</ShadTooltip>
|
||||
<div
|
||||
className={cn(
|
||||
"absolute top-[50%] z-10 h-3 w-3 translate-y-[-50%] rounded-full bg-background",
|
||||
left ? "-left-[4px] -ml-0.5" : "-right-[4px] -mr-0.5",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
import React from "react";
|
||||
import {
|
||||
INPUT_HANDLER_HOVER,
|
||||
OUTPUT_HANDLER_HOVER,
|
||||
} from "../../../../constants/constants";
|
||||
import {
|
||||
nodeColors,
|
||||
nodeIconsLucide,
|
||||
nodeNames,
|
||||
} from "../../../../utils/styleUtils";
|
||||
import { classNames } from "../../../../utils/utils";
|
||||
|
||||
const TooltipRenderComponent = ({ item, index, left }) => {
|
||||
const Icon = nodeIconsLucide[item.family] ?? nodeIconsLucide["unknown"];
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
data-testid={`available-${left ? "input" : "output"}-${item.family}`}
|
||||
>
|
||||
{index === 0 && (
|
||||
<span>{left ? INPUT_HANDLER_HOVER : OUTPUT_HANDLER_HOVER}</span>
|
||||
)}
|
||||
<span
|
||||
key={index}
|
||||
className={classNames(
|
||||
index > 0 ? "mt-2 flex items-center" : "mt-3 flex items-center",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className="h-5 w-5"
|
||||
style={{
|
||||
color: nodeColors[item.family],
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
className="h-5 w-5"
|
||||
strokeWidth={1.5}
|
||||
style={{
|
||||
color: nodeColors[item.family] ?? nodeColors.unknown,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
className="ps-2 text-xs text-foreground"
|
||||
data-testid={`tooltip-${nodeNames[item.family] ?? "Other"}`}
|
||||
>
|
||||
{nodeNames[item.family] ?? "Other"}{" "}
|
||||
{item?.display_name && item?.display_name?.length > 0 ? (
|
||||
<span
|
||||
className="text-xs"
|
||||
data-testid={`tooltip-${item?.display_name}`}
|
||||
>
|
||||
{" "}
|
||||
{item.display_name === "" ? "" : " - "}
|
||||
{item.display_name.split(", ").length > 2
|
||||
? item.display_name.split(", ").map((el, index) => (
|
||||
<React.Fragment key={el + name}>
|
||||
<span>
|
||||
{index === item.display_name.split(", ").length - 1
|
||||
? el
|
||||
: (el += `, `)}
|
||||
</span>
|
||||
</React.Fragment>
|
||||
))
|
||||
: item.display_name}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-xs" data-testid={`tooltip-${item?.type}`}>
|
||||
{" "}
|
||||
{item.type === "" ? "" : " - "}
|
||||
{item.type.split(", ").length > 2
|
||||
? item.type.split(", ").map((el, index) => (
|
||||
<React.Fragment key={el + name}>
|
||||
<span>
|
||||
{index === item.type.split(", ").length - 1
|
||||
? el
|
||||
: (el += `, `)}
|
||||
</span>
|
||||
</React.Fragment>
|
||||
))
|
||||
: item.type}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TooltipRenderComponent;
|
||||
|
|
@ -9,12 +9,19 @@ export default function ShadTooltip({
|
|||
children,
|
||||
styleClasses,
|
||||
delayDuration = 500,
|
||||
open,
|
||||
setOpen,
|
||||
}: ShadToolTipType): JSX.Element {
|
||||
return content ? (
|
||||
<Tooltip defaultOpen={!children} delayDuration={delayDuration}>
|
||||
<Tooltip
|
||||
defaultOpen={!children}
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
delayDuration={delayDuration}
|
||||
>
|
||||
<TooltipTrigger asChild={asChild}>{children}</TooltipTrigger>
|
||||
<TooltipContent
|
||||
className={cn(styleClasses, "max-w-96")}
|
||||
className={cn("max-w-96", styleClasses)}
|
||||
side={side}
|
||||
avoidCollisions={false}
|
||||
sticky="always"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { DefaultEdge } from "@/CustomEdges";
|
||||
import NoteNode from "@/CustomNodes/NoteNode";
|
||||
import IconComponent from "@/components/genericIconComponent";
|
||||
import LoadingComponent from "@/components/loadingComponent";
|
||||
|
|
@ -8,7 +9,6 @@ import useAutoSaveFlow from "@/hooks/flows/use-autosave-flow";
|
|||
import useUploadFlow from "@/hooks/flows/use-upload-flow";
|
||||
import { getNodeRenderType, isSupportedNodeTypes } from "@/utils/utils";
|
||||
|
||||
import { ENABLE_MVPS } from "@/customization/feature-flags";
|
||||
import _, { cloneDeep } from "lodash";
|
||||
import {
|
||||
KeyboardEvent,
|
||||
|
|
@ -457,6 +457,8 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
|
|||
onSelectionDragStart={onSelectionDragStart}
|
||||
onSelectionEnd={onSelectionEnd}
|
||||
onSelectionStart={onSelectionStart}
|
||||
connectionRadius={25}
|
||||
edgeTypes={{ default: DefaultEdge }}
|
||||
connectionLineComponent={ConnectionLineComponent}
|
||||
onDragOver={onDragOver}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export default function ParentDisclosureComponent({
|
|||
data-testid={testId}
|
||||
>
|
||||
<div className="flex items-baseline gap-1 align-baseline">
|
||||
<span className="parent-disclosure-title">{title}</span>
|
||||
<span className="text-sm font-medium">{title}</span>
|
||||
{beta && (
|
||||
<div className="h-fit rounded-full bg-beta-background px-2 py-1 text-xs/3 font-semibold text-beta-foreground-soft">
|
||||
BETA
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { nodeIconsLucide } from "../../../../utils/styleUtils";
|
|||
import ParentDisclosureComponent from "../ParentDisclosureComponent";
|
||||
import { SidebarCategoryComponent } from "./SidebarCategoryComponent";
|
||||
|
||||
import { SidebarFilterComponent } from "./sidebarFilterComponent";
|
||||
import { sortKeys } from "./utils";
|
||||
|
||||
export default function ExtraSidebar(): JSX.Element {
|
||||
|
|
@ -25,6 +26,7 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
const getFilterEdge = useFlowStore((state) => state.getFilterEdge);
|
||||
const setFilterEdge = useFlowStore((state) => state.setFilterEdge);
|
||||
const hasStore = useStoreStore((state) => state.hasStore);
|
||||
const filterType = useFlowStore((state) => state.filterType);
|
||||
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const [dataFilter, setFilterData] = useState(data);
|
||||
|
|
@ -222,8 +224,18 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
|
||||
<div className="side-bar-components-div-arrangement">
|
||||
<div className="parent-disclosure-arrangement">
|
||||
<div className="flex items-center gap-4 align-middle">
|
||||
<span className="parent-disclosure-title">Components</span>
|
||||
<div className="flex w-full flex-col items-start justify-between gap-2.5">
|
||||
<span className="text-sm font-medium">Components</span>
|
||||
{filterType && (
|
||||
<SidebarFilterComponent
|
||||
isInput={!!filterType.source}
|
||||
type={filterType.type}
|
||||
resetFilters={() => {
|
||||
setFilterEdge([]);
|
||||
setFilterData(data);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Separator />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
import ForwardedIconComponent from "@/components/genericIconComponent";
|
||||
import ShadTooltip from "@/components/shadTooltipComponent";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
export function SidebarFilterComponent({
|
||||
isInput,
|
||||
type,
|
||||
resetFilters,
|
||||
}: {
|
||||
isInput: boolean;
|
||||
type: string;
|
||||
resetFilters: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="mb-0.5 flex w-full items-center justify-between rounded border bg-muted p-1 px-2 text-xs font-medium text-primary">
|
||||
<div className="flex flex-1 items-center gap-1.5">
|
||||
<ForwardedIconComponent
|
||||
name="ListFilter"
|
||||
className="h-4 w-4 shrink-0 stroke-2"
|
||||
/>
|
||||
<div className="flex-1 overflow-hidden truncate">
|
||||
{isInput ? "Input" : "Output"}: {type}
|
||||
</div>
|
||||
</div>
|
||||
<ShadTooltip
|
||||
side="right"
|
||||
styleClasses="max-w-full"
|
||||
content="Remove filter"
|
||||
>
|
||||
<Button unstyled className="shrink-0" onClick={resetFilters}>
|
||||
<ForwardedIconComponent
|
||||
name="X"
|
||||
className="h-4 w-4 stroke-2"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -431,6 +431,9 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
});
|
||||
},
|
||||
setFilterEdge: (newState) => {
|
||||
if (newState.length === 0) {
|
||||
set({ filterType: undefined });
|
||||
}
|
||||
set({ getFilterEdge: newState });
|
||||
},
|
||||
getFilterEdge: [],
|
||||
|
|
@ -469,8 +472,6 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
targetHandle: scapeJSONParse(connection.targetHandle!),
|
||||
sourceHandle: scapeJSONParse(connection.sourceHandle!),
|
||||
},
|
||||
// style: { stroke: "#555" },
|
||||
// className: "stroke-foreground stroke-connection",
|
||||
},
|
||||
oldEdges,
|
||||
);
|
||||
|
|
@ -528,6 +529,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
get().updateBuildStatus(ids, BuildStatus.ERROR);
|
||||
throw new Error("Invalid components");
|
||||
}
|
||||
get().updateEdgesRunningByNodes(nodes, true);
|
||||
}
|
||||
function handleBuildUpdate(
|
||||
vertexBuildData: VertexBuildTypeAPI,
|
||||
|
|
@ -631,6 +633,10 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
});
|
||||
}
|
||||
}
|
||||
get().updateEdgesRunningByNodes(
|
||||
get().nodes.map((n) => n.id),
|
||||
false,
|
||||
);
|
||||
get().setIsBuilding(false);
|
||||
get().setLockChat(false);
|
||||
},
|
||||
|
|
@ -653,6 +659,10 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
title:
|
||||
"There are outdated components in the flow. The error could be related to them.",
|
||||
});
|
||||
get().updateEdgesRunningByNodes(
|
||||
get().nodes.map((n) => n.id),
|
||||
false,
|
||||
);
|
||||
setErrorData({ list, title });
|
||||
get().setIsBuilding(false);
|
||||
get().setLockChat(false);
|
||||
|
|
@ -662,7 +672,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
// reference is the id of the vertex or the id of the parent in a group node
|
||||
.map((element) => element.reference)
|
||||
.filter(Boolean) as string[];
|
||||
useFlowStore.getState().updateBuildStatus(idList, BuildStatus.BUILDING);
|
||||
get().updateBuildStatus(idList, BuildStatus.BUILDING);
|
||||
},
|
||||
onValidateNodes: validateSubgraph,
|
||||
nodes: get().nodes || undefined,
|
||||
|
|
@ -680,6 +690,17 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
viewport: get().reactFlowInstance?.getViewport()!,
|
||||
};
|
||||
},
|
||||
updateEdgesRunningByNodes: (ids: string[], running: boolean) => {
|
||||
const edges = get().edges;
|
||||
const newEdges = edges.map((edge) => {
|
||||
if (ids.includes(edge.source) && ids.includes(edge.target)) {
|
||||
edge.animated = running;
|
||||
edge.className = running ? "running" : "";
|
||||
}
|
||||
return edge;
|
||||
});
|
||||
set({ edges: newEdges });
|
||||
},
|
||||
updateVerticesBuild: (
|
||||
vertices: {
|
||||
verticesIds: string[];
|
||||
|
|
@ -762,6 +783,15 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
setBuildController: (controller) => {
|
||||
set({ buildController: controller });
|
||||
},
|
||||
handleDragging: undefined,
|
||||
setHandleDragging: (handleDragging) => {
|
||||
set({ handleDragging });
|
||||
},
|
||||
|
||||
filterType: undefined,
|
||||
setFilterType: (filterType) => {
|
||||
set({ filterType });
|
||||
},
|
||||
}));
|
||||
|
||||
export default useFlowStore;
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@
|
|||
@apply fill-chat-trigger-disabled stroke-chat-trigger-disabled stroke-1;
|
||||
}
|
||||
.parent-disclosure-arrangement {
|
||||
@apply flex w-full select-none items-center justify-between bg-background px-3 py-1;
|
||||
@apply flex w-full select-none items-center justify-between bg-background px-5 py-3;
|
||||
}
|
||||
.components-disclosure-arrangement {
|
||||
@apply -mt-px flex w-full select-none items-center justify-between border-y border-y-input bg-muted px-3 py-2;
|
||||
|
|
@ -240,9 +240,6 @@
|
|||
/* different color than the non child */
|
||||
@apply -mt-px flex w-full select-none items-center justify-between border-y border-y-input bg-muted px-3 py-2;
|
||||
}
|
||||
.parent-disclosure-title {
|
||||
@apply p-2 px-2 text-sm font-medium;
|
||||
}
|
||||
.components-disclosure-title {
|
||||
@apply flex items-center text-sm text-primary;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,6 +169,26 @@ textarea[class^="ag-"]:focus {
|
|||
cursor: grabbing !important;
|
||||
}
|
||||
|
||||
.react-flow__handle-right {
|
||||
right: 0 !important;
|
||||
transform: translate(50%, -50%) !important;
|
||||
}
|
||||
|
||||
.react-flow__handle-left {
|
||||
left: 0 !important;
|
||||
transform: translate(-50%, -50%) !important;
|
||||
}
|
||||
|
||||
.react-flow__handle-right {
|
||||
right: 0 !important;
|
||||
transform: translate(50%, -50%) !important;
|
||||
}
|
||||
|
||||
.react-flow__handle-left {
|
||||
left: 0 !important;
|
||||
transform: translate(-50%, -50%) !important;
|
||||
}
|
||||
|
||||
.react-flow__node-noteNode:not(.selected) {
|
||||
z-index: -1 !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@
|
|||
--hover: #f2f4f5;
|
||||
--disabled-run: #6366f1;
|
||||
|
||||
--filter-foreground: #4f46e5;
|
||||
--filter-background: #eef2ff;
|
||||
/* Colors that are shared in dark and light mode */
|
||||
--blur-shared: #151923de;
|
||||
--build-trigger: #dc735b;
|
||||
|
|
@ -114,6 +116,9 @@
|
|||
--destructive: 0 60% 25%; /* hsl(0 60% 25%) */
|
||||
--destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
|
||||
|
||||
--filter-foreground: #eef2ff;
|
||||
--filter-background: #4e46e599;
|
||||
|
||||
--ring: 216 24% 30%; /* hsl(216 24% 30%) */
|
||||
|
||||
--radius: 0.5rem;
|
||||
|
|
|
|||
|
|
@ -339,6 +339,8 @@ export type ShadTooltipProps = {
|
|||
style?: string;
|
||||
};
|
||||
export type ShadToolTipType = {
|
||||
open?: boolean;
|
||||
setOpen?: (open: boolean) => void;
|
||||
content?: ReactNode | null;
|
||||
side?: "top" | "right" | "bottom" | "left";
|
||||
asChild?: boolean;
|
||||
|
|
|
|||
|
|
@ -180,6 +180,52 @@ export type FlowStoreType = {
|
|||
edges?: Edge[];
|
||||
viewport?: Viewport;
|
||||
}) => void;
|
||||
handleDragging:
|
||||
| {
|
||||
source: string | undefined;
|
||||
sourceHandle: string | undefined;
|
||||
target: string | undefined;
|
||||
targetHandle: string | undefined;
|
||||
type: string;
|
||||
color: string;
|
||||
}
|
||||
| undefined;
|
||||
setHandleDragging: (
|
||||
data:
|
||||
| {
|
||||
source: string | undefined;
|
||||
sourceHandle: string | undefined;
|
||||
target: string | undefined;
|
||||
targetHandle: string | undefined;
|
||||
type: string;
|
||||
color: string;
|
||||
}
|
||||
| undefined,
|
||||
) => void;
|
||||
|
||||
filterType:
|
||||
| {
|
||||
source: string | undefined;
|
||||
sourceHandle: string | undefined;
|
||||
target: string | undefined;
|
||||
targetHandle: string | undefined;
|
||||
type: string;
|
||||
color: string;
|
||||
}
|
||||
| undefined;
|
||||
setFilterType: (
|
||||
data:
|
||||
| {
|
||||
source: string | undefined;
|
||||
sourceHandle: string | undefined;
|
||||
target: string | undefined;
|
||||
targetHandle: string | undefined;
|
||||
type: string;
|
||||
color: string;
|
||||
}
|
||||
| undefined,
|
||||
) => void;
|
||||
updateEdgesRunningByNodes: (ids: string[], running: boolean) => void;
|
||||
stopBuilding: () => void;
|
||||
buildController: AbortController;
|
||||
setBuildController: (controller: AbortController) => void;
|
||||
|
|
|
|||
|
|
@ -215,6 +215,9 @@ export function isValidConnection(
|
|||
nodes: Node[],
|
||||
edges: Edge[],
|
||||
) {
|
||||
if (source === target) {
|
||||
return false;
|
||||
}
|
||||
const targetHandleObject: targetHandleType = scapeJSONParse(targetHandle!);
|
||||
const sourceHandleObject: sourceHandleType = scapeJSONParse(sourceHandle!);
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
AlertTriangle,
|
||||
ArrowBigUp,
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
ArrowUpToLine,
|
||||
Bell,
|
||||
Binary,
|
||||
|
|
@ -63,6 +64,7 @@ import {
|
|||
FileText,
|
||||
FileType2,
|
||||
FileUp,
|
||||
Filter,
|
||||
FlaskConical,
|
||||
FolderIcon,
|
||||
FolderPlus,
|
||||
|
|
@ -87,6 +89,7 @@ import {
|
|||
Layers,
|
||||
Link,
|
||||
Link2,
|
||||
ListFilter,
|
||||
Loader2,
|
||||
Lock,
|
||||
LogIn,
|
||||
|
|
@ -401,10 +404,12 @@ export const nodeIconsLucide: iconsType = {
|
|||
GoogleSearchRun: GoogleIcon,
|
||||
Google: GoogleIcon,
|
||||
GoogleGenerativeAI: GoogleGenerativeAIIcon,
|
||||
ArrowRight,
|
||||
Groq: GroqIcon,
|
||||
HCD: HCDIcon,
|
||||
HNLoader: HackerNewsIcon,
|
||||
Unstructured: UnstructuredIcon,
|
||||
Filter: Filter,
|
||||
HuggingFaceHub: HuggingFaceIcon,
|
||||
HuggingFace: HuggingFaceIcon,
|
||||
HuggingFaceEmbeddings: HuggingFaceIcon,
|
||||
|
|
@ -414,6 +419,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
Meta: MetaIcon,
|
||||
CheckCheck,
|
||||
Midjorney: MidjourneyIcon,
|
||||
ListFilter,
|
||||
MongoDBAtlasVectorSearch: MongoDBIcon,
|
||||
MongoDB: MongoDBIcon,
|
||||
MongoDBChatMessageHistory: MongoDBIcon,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue