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:
parent
ffd9eaae9f
commit
c9e0d5e4ba
16 changed files with 281 additions and 269 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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[];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -353,7 +353,7 @@ const config = {
|
|||
borderRadius: "999px",
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb:hover": {
|
||||
backgroundColor: "hsl(var(--ring))",
|
||||
backgroundColor: "hsl(var(--placeholder-foreground))",
|
||||
},
|
||||
cursor: "auto",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue