fix: updated node toolbar to be more stable on zoom, fix success and error component tooltips, unselect nodes on flow opening (#4610)

* Unselect nodes and edges on enter

* Start with viewport fit

* Fix run component tooltip to match error and success

* Update scrollbar design

* Added node toolbar without portal to match the width of the node

* Updated color of scrollbar

* Updated NoteNode to have same behavior as GenericNode

* Updated logic for tests to pass

* Fix fitview to only occur when nodes are present

* Update z index of resize handle

* Sticky notes test fix

* [autofix.ci] apply automated fixes

* freeze test fixed

---------

Co-authored-by: Mike Fortman <michael.fortman@datastax.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: anovazzi1 <otavio2204@gmail.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
Co-authored-by: Cristhian Zanforlin Lousa <cristhian.lousa@gmail.com>
This commit is contained in:
Lucas Oliveira 2024-11-19 18:54:49 -03:00 • committed by GitHub
commit c9e0d5e4ba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 281 additions and 269 deletions

View file

@ -93,7 +93,7 @@ body {
}
::-webkit-scrollbar-thumb:hover {
background-color: hsl(var(--ring)) !important;
background-color: hsl(var(--placeholder-foreground)) !important;
}
.jv-indent::-webkit-scrollbar-track {
@ -107,7 +107,7 @@ body {
}
.jv-indent::-webkit-scrollbar-thumb:hover {
background-color: hsl(var(--ring)) !important;
background-color: hsl(var(--placeholder-foreground)) !important;
}
.custom-hover {
@ -170,7 +170,7 @@ body {
height: 0.75rem !important;
background-color: white !important;
border-color: var(--border) !important;
z-index: 1000 !important;
z-index: 50 !important;
border-radius: 20% !important;
}

View file

@ -151,7 +151,6 @@ export default function NodeStatus({
const divRef = useRef<HTMLDivElement>(null);
const [isHovered, setIsHovered] = useState(false);
const runClass = "justify-left flex font-normal text-muted-foreground";
const stopBuilding = useFlowStore((state) => state.stopBuilding);
const handleClickRun = () => {
@ -207,6 +206,12 @@ export default function NodeStatus({
<div className="flex items-center gap-2 self-center">
<ShadTooltip
styleClasses={cn(
"border rounded-xl",
conditionSuccess
? "border-accent-emerald-foreground bg-success-background"
: "border-destructive bg-error-background",
)}
content={
buildStatus === BuildStatus.BUILDING ? (
<span> {STATUS_BUILDING} </span>
@ -215,26 +220,26 @@ export default function NodeStatus({
) : !validationStatus ? (
<span className="flex">{STATUS_BUILD}</span>
) : (
<div className="max-h-100 p-2">
<div className="max-h-80 overflow-auto">
<div className="max-h-100 px-1 py-2.5">
<div className="flex max-h-80 flex-col gap-2 overflow-auto">
{validationString && (
<div className="text-accent-red-foreground ml-1 pb-2">
<div className="text-sm text-foreground">
{validationString}
</div>
)}
{lastRunTime && (
<div className={runClass}>
<div className="flex items-center text-sm text-secondary-foreground">
<div>{RUN_TIMESTAMP_PREFIX}</div>
<div className="ml-1 text-status-blue">
<div className="ml-1 text-secondary-foreground">
{lastRunTime}
</div>
</div>
)}
</div>
<div className={runClass}>
<div>Duration:</div>
<div className="ml-1 text-status-blue">
{validationStatus?.data.duration}
<div className="flex items-center text-secondary-foreground">
<div>Duration:</div>
<div className="ml-1">
{validationStatus?.data.duration}
</div>
</div>
</div>
</div>

View file

@ -1,7 +1,7 @@
import { usePostValidateComponentCode } from "@/controllers/API/queries/nodes/use-post-validate-component-code";
import { useEffect, useMemo, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
import { useUpdateNodeInternals } from "reactflow";
import { ForwardedIconComponent } from "../../components/genericIconComponent";
import ShadTooltip from "../../components/shadTooltipComponent";
import { Button } from "../../components/ui/button";
@ -214,8 +214,8 @@ export default function GenericNode({
}, [hiddenOutputs]);
const memoizedNodeToolbarComponent = useMemo(() => {
return (
<NodeToolbar>
return selected ? (
<div className={cn("absolute -top-12 left-1/2 z-50 -translate-x-1/2")}>
<NodeToolbarComponent
data={data}
deleteNode={(id) => {
@ -236,7 +236,9 @@ export default function GenericNode({
isOutdated={isOutdated && isUserEdited}
setOpenShowMoreOptions={setOpenShowMoreOptions}
/>
</NodeToolbar>
</div>
) : (
<></>
);
}, [
data,
@ -336,7 +338,6 @@ export default function GenericNode({
return (
<>
{memoizedNodeToolbarComponent}
<div
className={cn(
borderColor,
@ -345,9 +346,9 @@ export default function GenericNode({
: `h-[4.065rem] w-48 rounded-[0.75rem] ${!selected ? "border-[1px] border-border ring-[0.5px] ring-border" : ""}`,
"generic-node-div group/node relative",
!hasOutputs && "pb-4",
openShowMoreOptions && "nowheel",
)}
>
{memoizedNodeToolbarComponent}
<div
data-testid={`${data.id}-main-node`}
className={cn(

View file

@ -7,7 +7,7 @@ import {
} from "@/components/ui/popover";
import {
Select,
SelectContent,
SelectContentWithoutPortal,
SelectItem,
SelectTrigger,
} from "@/components/ui/select-custom";
@ -84,7 +84,7 @@ export default function NoteToolbarComponent({
<div className="w-26 noflow nowheel nopan nodelete nodrag h-10">
<span className="isolate inline-flex rounded-md shadow-sm">
<Popover>
<ShadTooltip content="Color pick">
<ShadTooltip content="Pick Color">
<PopoverTrigger>
<div>
<div
@ -145,8 +145,8 @@ export default function NoteToolbarComponent({
</PopoverContent>
</Popover>
<Select onValueChange={handleSelectChange} value="">
<ShadTooltip content="All" side="top">
<SelectTrigger>
<SelectTrigger>
<ShadTooltip content="Show More" side="top">
<div>
<div
data-testid="more-options-modal"
@ -160,9 +160,9 @@ export default function NoteToolbarComponent({
/>
</div>
</div>
</SelectTrigger>
</ShadTooltip>
<SelectContent>
</ShadTooltip>
</SelectTrigger>
<SelectContentWithoutPortal>
<SelectItem value={"duplicate"}>
<ToolbarSelectItem
shortcut={
@ -211,7 +211,7 @@ export default function NoteToolbarComponent({
</span>
</div>
</SelectItem>
</SelectContent>
</SelectContentWithoutPortal>
</Select>
</span>
</div>

View file

@ -8,7 +8,7 @@ import {
import { noteDataType } from "@/types/flow";
import { cn } from "@/utils/utils";
import { useEffect, useMemo, useRef, useState } from "react";
import { NodeResizer, NodeToolbar } from "reactflow";
import { NodeResizer } from "reactflow";
import NodeDescription from "../GenericNode/components/NodeDescription";
import NoteToolbarComponent from "./NoteToolbarComponent";
function NoteNode({
@ -33,17 +33,20 @@ function NoteNode({
});
}
}, []);
const MemoNoteToolbarComponent = useMemo(
() => (
<NodeToolbar>
<NoteToolbarComponent data={data} bgColor={bgColor} />
</NodeToolbar>
),
[data, bgColor],
() =>
selected ? (
<div className={cn("absolute -top-12 left-1/2 z-50 -translate-x-1/2")}>
<NoteToolbarComponent data={data} bgColor={bgColor} />
</div>
) : (
<></>
),
[data, bgColor, selected],
);
return (
<>
{MemoNoteToolbarComponent}
<NodeResizer
minWidth={NOTE_NODE_MIN_WIDTH}
minHeight={NOTE_NODE_MIN_HEIGHT}
@ -67,11 +70,12 @@ function NoteNode({
}}
ref={nodeDiv}
className={cn(
"flex h-full w-full flex-col gap-3 rounded-xl p-3 transition-all",
"relative flex h-full w-full flex-col gap-3 rounded-xl p-3 transition-all",
COLOR_OPTIONS[bgColor] !== null &&
`border ${!selected && "-z-50 shadow-sm"}`,
)}
>
{MemoNoteToolbarComponent}
<div
style={{
width: size.width,

View file

@ -55,6 +55,34 @@ const SelectContent = React.forwardRef<
));
SelectContent.displayName = SelectPrimitive.Content.displayName;
const SelectContentWithoutPortal = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 min-w-[11.5rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className,
)}
position={position}
{...props}
>
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
)}
>
{children}
</SelectPrimitive.Viewport>
</SelectPrimitive.Content>
));
SelectContentWithoutPortal.displayName = SelectPrimitive.Content.displayName;
const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
@ -99,6 +127,7 @@ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
export {
Select,
SelectContent,
SelectContentWithoutPortal,
SelectGroup,
SelectItem,
SelectLabel,

View file

@ -91,6 +91,7 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
);
const nodes = useFlowStore((state) => state.nodes);
const edges = useFlowStore((state) => state.edges);
const isEmptyFlow = useRef(nodes.length === 0);
const onNodesChange = useFlowStore((state) => state.onNodesChange);
const onEdgesChange = useFlowStore((state) => state.onEdgesChange);
const setNodes = useFlowStore((state) => state.setNodes);
@ -552,6 +553,7 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
onDrop={onDrop}
onSelectionChange={onSelectionChange}
deleteKeyCode={[]}
fitView={isEmptyFlow.current ? false : true}
className="theme-attribution"
minZoom={0.01}
maxZoom={8}

View file

@ -11,12 +11,12 @@ import CodeAreaModal from "@/modals/codeAreaModal";
import { APIClassType } from "@/types/api";
import _, { cloneDeep } from "lodash";
import { useEffect, useRef, useState } from "react";
import { useStore, useUpdateNodeInternals } from "reactflow";
import { useUpdateNodeInternals } from "reactflow";
import IconComponent from "../../../../components/genericIconComponent";
import ShadTooltip from "../../../../components/shadTooltipComponent";
import {
Select,
SelectContent,
SelectContentWithoutPortal,
SelectItem,
SelectTrigger,
} from "../../../../components/ui/select-custom";
@ -370,31 +370,9 @@ export default function NodeToolbarComponent({
parameterId: "tool_mode",
});
// Use ReactFlow's store selector to get zoom updates
const zoom = useStore((state) => state.transform[2]);
const [scale, setScale] = useState<number | null>(null);
useEffect(() => {
if (!zoom) return;
if (zoom < 0.65) {
const newScale = Math.max(zoom * 1.2, 0.4);
setScale(newScale);
} else {
setScale(1);
}
}, [zoom]);
if (scale === null) return <></>;
return (
<>
<div
className="noflow nowheel nopan nodelete nodrag"
style={{
transform: `scale(${scale})`,
transformOrigin: "bottom",
}}
>
<div className="noflow nopan nodelete nodrag">
<div className="toolbar-wrapper">
{hasCode && (
<ShadTooltip
@ -406,7 +384,6 @@ export default function NodeToolbarComponent({
/>
}
side="top"
styleClasses="relative bottom-2"
>
<Button
className="node-toolbar-buttons"
@ -435,7 +412,6 @@ export default function NodeToolbarComponent({
/>
}
side="top"
styleClasses="relative bottom-2"
>
<Button
className="node-toolbar-buttons"
@ -533,225 +509,223 @@ export default function NodeToolbarComponent({
</Button>
</ShadTooltip>
)}
<ShadTooltip
content="Show More"
side="top"
styleClasses="relative bottom-2"
>
<Button
className="node-toolbar-buttons h-[2rem] w-[2rem]"
variant="ghost"
onClick={handleButtonClick}
size="node-toolbar"
data-testid="more-options-modal"
>
<IconComponent name="MoreHorizontal" className="h-4 w-4" />
</Button>
</ShadTooltip>
</div>
<Select
onValueChange={handleSelectChange}
value={selectedValue!}
onOpenChange={handleOpenChange}
>
<SelectTrigger ref={selectTriggerRef} className="w-62">
<></>
</SelectTrigger>
<SelectContent
className={"relative top-1 w-56 bg-background"}
style={{
transform: `scale(${scale})`,
transformOrigin: "top",
left: scale === 1 ? "4.5rem" : `${1.7 * (scale - 1)}rem`,
}}
<Select
onValueChange={handleSelectChange}
value={selectedValue!}
onOpenChange={handleOpenChange}
>
{hasCode && (
<SelectItem value={"code"}>
<SelectTrigger className="w-62">
<ShadTooltip content="Show More" side="top">
<div>
<Button
className="node-toolbar-buttons h-[2rem] w-[2rem]"
variant="ghost"
onClick={handleButtonClick}
size="node-toolbar"
data-testid="more-options-modal"
>
<IconComponent name="MoreHorizontal" className="h-4 w-4" />
</Button>
</div>
</ShadTooltip>
</SelectTrigger>
<SelectContentWithoutPortal
className={"relative top-1 w-56 bg-background"}
>
{hasCode && (
<SelectItem value={"code"}>
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Code")?.shortcut!
}
value={"Code"}
icon={"Code"}
dataTestId="code-button-modal"
/>
</SelectItem>
)}
{nodeLength > 0 && (
<SelectItem value={nodeLength === 0 ? "disabled" : "advanced"}>
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Advanced Settings")
?.shortcut!
}
value={"Controls"}
icon={"SlidersHorizontal"}
dataTestId="advanced-button-modal"
/>
</SelectItem>
)}
<SelectItem value={"save"}>
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Code")?.shortcut!
}
value={"Code"}
icon={"Code"}
dataTestId="code-button-modal"
/>
</SelectItem>
)}
{nodeLength > 0 && (
<SelectItem value={nodeLength === 0 ? "disabled" : "advanced"}>
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Advanced Settings")
shortcuts.find((obj) => obj.name === "Save Component")
?.shortcut!
}
value={"Controls"}
icon={"SlidersHorizontal"}
dataTestId="advanced-button-modal"
value={"Save"}
icon={"SaveAll"}
dataTestId="save-button-modal"
/>
</SelectItem>
)}
<SelectItem value={"save"}>
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Save Component")
?.shortcut!
}
value={"Save"}
icon={"SaveAll"}
dataTestId="save-button-modal"
/>
</SelectItem>
<SelectItem value={"duplicate"}>
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Duplicate")?.shortcut!
}
value={"Duplicate"}
icon={"Copy"}
dataTestId="copy-button-modal"
/>
</SelectItem>
<SelectItem value={"copy"}>
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Copy")?.shortcut!
}
value={"Copy"}
icon={"Clipboard"}
dataTestId="copy-button-modal"
/>
</SelectItem>
{isOutdated && (
<SelectItem value={"update"}>
<SelectItem value={"duplicate"}>
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Update")?.shortcut!
shortcuts.find((obj) => obj.name === "Duplicate")?.shortcut!
}
value={"Restore"}
icon={"RefreshCcwDot"}
dataTestId="update-button-modal"
value={"Duplicate"}
icon={"Copy"}
dataTestId="copy-button-modal"
/>
</SelectItem>
)}
{hasStore && (
<SelectItem value={"Share"} disabled={!hasApiKey || !validApiKey}>
<SelectItem value={"copy"}>
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Component Share")
?.shortcut!
shortcuts.find((obj) => obj.name === "Copy")?.shortcut!
}
value={"Copy"}
icon={"Clipboard"}
dataTestId="copy-button-modal"
/>
</SelectItem>
{isOutdated && (
<SelectItem value={"update"}>
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Update")?.shortcut!
}
value={"Restore"}
icon={"RefreshCcwDot"}
dataTestId="update-button-modal"
/>
</SelectItem>
)}
{hasStore && (
<SelectItem
value={"Share"}
icon={"Share3"}
dataTestId="share-button-modal"
/>
</SelectItem>
)}
disabled={!hasApiKey || !validApiKey}
>
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Component Share")
?.shortcut!
}
value={"Share"}
icon={"Share3"}
dataTestId="share-button-modal"
/>
</SelectItem>
)}
<SelectItem
value={"documentation"}
disabled={data.node?.documentation === ""}
>
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Docs")?.shortcut!
}
value={"Docs"}
icon={"FileText"}
dataTestId="docs-button-modal"
/>
</SelectItem>
{(isMinimal || !showNode) && (
<SelectItem
value={"show"}
data-testid={`${showNode ? "minimize" : "expand"}-button-modal`}
value={"documentation"}
disabled={data.node?.documentation === ""}
>
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Minimize")?.shortcut!
shortcuts.find((obj) => obj.name === "Docs")?.shortcut!
}
value={showNode ? "Minimize" : "Expand"}
icon={showNode ? "Minimize2" : "Maximize2"}
dataTestId="minimize-button-modal"
value={"Docs"}
icon={"FileText"}
dataTestId="docs-button-modal"
/>
</SelectItem>
)}
{isGroup && (
<SelectItem value="ungroup">
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Group")?.shortcut!
}
value={"Ungroup"}
icon={"Ungroup"}
dataTestId="group-button-modal"
/>
</SelectItem>
)}
<SelectItem value="freeze">
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Freeze")?.shortcut!
}
value={"Freeze"}
icon={"Snowflake"}
dataTestId="freeze-button"
style={`${frozen ? " text-ice" : ""} transition-all`}
/>
</SelectItem>
<SelectItem value="freezeAll">
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Freeze Path")?.shortcut!
}
value={"Freeze Path"}
icon={"FreezeAll"}
dataTestId="freeze-path-button"
style={`${frozen ? " text-ice" : ""} transition-all`}
/>
</SelectItem>
<SelectItem value="Download">
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Download")?.shortcut!
}
value={"Download"}
icon={"Download"}
dataTestId="download-button-modal"
/>
</SelectItem>
<SelectItem value={"delete"} className="focus:bg-red-400/[.20]">
<div className="font-red flex text-status-red">
<IconComponent
name="Trash2"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
<span className="">Delete</span>{" "}
<span
className={`absolute right-2 top-2 flex items-center justify-center rounded-sm px-1 py-[0.2]`}
{(isMinimal || !showNode) && (
<SelectItem
value={"show"}
data-testid={`${showNode ? "minimize" : "expand"}-button-modal`}
>
<IconComponent
name="Delete"
className="h-4 w-4 stroke-2 text-red-400"
></IconComponent>
</span>
</div>
</SelectItem>
{hasToolMode && (
<SelectItem value="toolMode">
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Minimize")
?.shortcut!
}
value={showNode ? "Minimize" : "Expand"}
icon={showNode ? "Minimize2" : "Maximize2"}
dataTestId="minimize-button-modal"
/>
</SelectItem>
)}
{isGroup && (
<SelectItem value="ungroup">
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Group")?.shortcut!
}
value={"Ungroup"}
icon={"Ungroup"}
dataTestId="group-button-modal"
/>
</SelectItem>
)}
<SelectItem value="freeze">
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Tool Mode")?.shortcut!
shortcuts.find((obj) => obj.name === "Freeze")?.shortcut!
}
value={"Tool Mode"}
icon={"Hammer"}
dataTestId="tool-mode-button"
style={`${toolMode ? "text-primary" : ""} transition-all`}
value={"Freeze"}
icon={"Snowflake"}
dataTestId="freeze-button"
style={`${frozen ? " text-ice" : ""} transition-all`}
/>
</SelectItem>
)}
</SelectContent>
</Select>
<SelectItem value="freezeAll">
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Freeze Path")
?.shortcut!
}
value={"Freeze Path"}
icon={"FreezeAll"}
dataTestId="freeze-path-button"
style={`${frozen ? " text-ice" : ""} transition-all`}
/>
</SelectItem>
<SelectItem value="Download">
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Download")?.shortcut!
}
value={"Download"}
icon={"Download"}
dataTestId="download-button-modal"
/>
</SelectItem>
<SelectItem value={"delete"} className="focus:bg-red-400/[.20]">
<div className="font-red flex text-status-red">
<IconComponent
name="Trash2"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
<span className="">Delete</span>{" "}
<span
className={`absolute right-2 top-2 flex items-center justify-center rounded-sm px-1 py-[0.2]`}
>
<IconComponent
name="Delete"
className="h-4 w-4 stroke-2 text-red-400"
></IconComponent>
</span>
</div>
</SelectItem>
{hasToolMode && (
<SelectItem value="toolMode">
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Tool Mode")
?.shortcut!
}
value={"Tool Mode"}
icon={"Hammer"}
dataTestId="tool-mode-button"
style={`${toolMode ? "text-primary" : ""} transition-all`}
/>
</SelectItem>
)}
</SelectContentWithoutPortal>
</Select>
</div>
<ConfirmationModal
open={showOverrideModal}

View file

@ -39,6 +39,7 @@ import {
getNodeId,
scapeJSONParse,
scapedJSONStringfy,
unselectAllNodesEdges,
updateGroupRecursion,
validateNodes,
} from "../utils/reactflowUtils";
@ -186,6 +187,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
let newEdges = cleanEdges(nodes, edges);
const { inputs, outputs } = getInputsAndOutputs(nodes);
get().updateComponentsToUpdate(nodes);
unselectAllNodesEdges(nodes, edges);
set({
nodes,
edges: newEdges,
@ -212,7 +214,6 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
},
setReactFlowInstance: (newState) => {
set({ reactFlowInstance: newState });
get().reactFlowInstance?.fitView();
},
onNodesChange: (changes: NodeChange[]) => {
set({

View file

@ -68,7 +68,7 @@
.ag-body-horizontal-scroll-viewport::-webkit-scrollbar-thumb:hover,
.ag-body-vertical-scroll-viewport::-webkit-scrollbar-thumb:hover {
background-color: hsl(var(--ring));
background-color: hsl(var(--placeholder-foreground));
}
.ag-paging-page-size {

View file

@ -26,6 +26,7 @@ pre {
}
.react-flow__pane {
pointer-events: all;
cursor: default;
}
@ -147,7 +148,7 @@ textarea[class^="ag-"]:focus {
}
.ace_scrollbar::-webkit-scrollbar-thumb:hover {
background-color: hsl(var(--ring));
background-color: hsl(var(--placeholder-foreground));
border-radius: 999px;
}

View file

@ -1,11 +1,6 @@
import { Edge, Node } from "reactflow";
import { Edge } from "reactflow";
import { FlowType, NodeType } from "../flow";
export type unselectAllNodesType = {
updateNodes: (nodes: Node[]) => void;
data: Node[];
};
export type addEscapedHandleIdsToEdgesType = {
edges: Edge[];
};

View file

@ -41,7 +41,6 @@ import {
addEscapedHandleIdsToEdgesType,
findLastNodeType,
generateFlowType,
unselectAllNodesType,
updateEdgesHandleIdsType,
} from "../types/utils/reactflowUtils";
import { getLayoutedNodes } from "./layoutUtils";
@ -202,12 +201,13 @@ export function detectBrokenEdgesEdges(nodes: NodeType[], edges: Edge[]) {
return BrokenEdges;
}
export function unselectAllNodes({ updateNodes, data }: unselectAllNodesType) {
let newNodes = cloneDeep(data);
newNodes.forEach((node: Node) => {
export function unselectAllNodesEdges(nodes: Node[], edges: Edge[]) {
nodes.forEach((node: Node) => {
node.selected = false;
});
updateNodes(newNodes!);
edges.forEach((edge: Edge) => {
edge.selected = false;
});
}
export function isValidConnection(

View file

@ -353,7 +353,7 @@ const config = {
borderRadius: "999px",
},
"&::-webkit-scrollbar-thumb:hover": {
backgroundColor: "hsl(var(--ring))",
backgroundColor: "hsl(var(--placeholder-foreground))",
},
cursor: "auto",
},

View file

@ -305,7 +305,7 @@ test("user must be able to freeze a component", async ({ page }) => {
await page.waitForTimeout(1000);
await page.getByTestId("icon-Snowflake").last().click();
await page.getByText("Freeze", { exact: true }).click();
await page.waitForTimeout(1000);

View file

@ -162,7 +162,7 @@ The future of AI is both exciting and uncertain. As the technology continues to
await page.getByTestId("note_node").nth(0).focus();
await page.getByTestId("more-options-modal").click();
await page.getByText("Delete").last().click();
await page.getByText("Delete").first().click();
await page.waitForTimeout(1000);