Make label changes unique by adding text (#840)
This commit is contained in:
@@ -56,7 +56,8 @@ function WorkflowHeader({
|
|||||||
editable={true}
|
editable={true}
|
||||||
onChange={onTitleChange}
|
onChange={onTitleChange}
|
||||||
value={title}
|
value={title}
|
||||||
className="text-3xl"
|
titleClassName="text-3xl"
|
||||||
|
inputClassName="text-3xl"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex h-full w-1/3 items-center justify-end gap-4 p-4">
|
<div className="flex h-full w-1/3 items-center justify-end gap-4 p-4">
|
||||||
|
|||||||
@@ -13,12 +13,15 @@ import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
|||||||
import { NodeActionMenu } from "../NodeActionMenu";
|
import { NodeActionMenu } from "../NodeActionMenu";
|
||||||
import type { CodeBlockNode } from "./types";
|
import type { CodeBlockNode } from "./types";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { getUpdatedNodesAfterLabelUpdateForParameterKeys } from "../../workflowEditorUtils";
|
import {
|
||||||
|
getLabelForExistingNode,
|
||||||
|
getUpdatedNodesAfterLabelUpdateForParameterKeys,
|
||||||
|
} from "../../workflowEditorUtils";
|
||||||
import { AppNode } from "..";
|
import { AppNode } from "..";
|
||||||
|
|
||||||
function CodeBlockNode({ id, data }: NodeProps<CodeBlockNode>) {
|
function CodeBlockNode({ id, data }: NodeProps<CodeBlockNode>) {
|
||||||
const { updateNodeData, setNodes } = useReactFlow();
|
const { updateNodeData, setNodes } = useReactFlow();
|
||||||
const nodes = useNodes();
|
const nodes = useNodes<AppNode>();
|
||||||
const deleteNodeCallback = useDeleteNodeCallback();
|
const deleteNodeCallback = useDeleteNodeCallback();
|
||||||
const [label, setLabel] = useState(data.label);
|
const [label, setLabel] = useState(data.label);
|
||||||
const [inputs, setInputs] = useState({
|
const [inputs, setInputs] = useState({
|
||||||
@@ -50,16 +53,22 @@ function CodeBlockNode({ id, data }: NodeProps<CodeBlockNode>) {
|
|||||||
value={label}
|
value={label}
|
||||||
editable={data.editable}
|
editable={data.editable}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setLabel(value);
|
const existingLabels = nodes.map((n) => n.data.label);
|
||||||
updateNodeData(id, { label: value });
|
const newLabel = getLabelForExistingNode(
|
||||||
|
value,
|
||||||
|
existingLabels,
|
||||||
|
);
|
||||||
|
setLabel(newLabel);
|
||||||
setNodes(
|
setNodes(
|
||||||
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
||||||
id,
|
id,
|
||||||
value,
|
newLabel,
|
||||||
nodes as Array<AppNode>,
|
nodes as Array<AppNode>,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
titleClassName="text-base"
|
||||||
|
inputClassName="text-base"
|
||||||
/>
|
/>
|
||||||
<span className="text-xs text-slate-400">Code Block</span>
|
<span className="text-xs text-slate-400">Code Block</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,12 +13,15 @@ import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
|||||||
import { NodeActionMenu } from "../NodeActionMenu";
|
import { NodeActionMenu } from "../NodeActionMenu";
|
||||||
import type { DownloadNode } from "./types";
|
import type { DownloadNode } from "./types";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { getUpdatedNodesAfterLabelUpdateForParameterKeys } from "../../workflowEditorUtils";
|
import {
|
||||||
|
getLabelForExistingNode,
|
||||||
|
getUpdatedNodesAfterLabelUpdateForParameterKeys,
|
||||||
|
} from "../../workflowEditorUtils";
|
||||||
import { AppNode } from "..";
|
import { AppNode } from "..";
|
||||||
|
|
||||||
function DownloadNode({ id, data }: NodeProps<DownloadNode>) {
|
function DownloadNode({ id, data }: NodeProps<DownloadNode>) {
|
||||||
const { updateNodeData, setNodes } = useReactFlow();
|
const { setNodes } = useReactFlow();
|
||||||
const nodes = useNodes();
|
const nodes = useNodes<AppNode>();
|
||||||
const deleteNodeCallback = useDeleteNodeCallback();
|
const deleteNodeCallback = useDeleteNodeCallback();
|
||||||
const [label, setLabel] = useState(data.label);
|
const [label, setLabel] = useState(data.label);
|
||||||
|
|
||||||
@@ -47,16 +50,22 @@ function DownloadNode({ id, data }: NodeProps<DownloadNode>) {
|
|||||||
value={label}
|
value={label}
|
||||||
editable={data.editable}
|
editable={data.editable}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setLabel(value);
|
const existingLabels = nodes.map((n) => n.data.label);
|
||||||
updateNodeData(id, { label: value });
|
const newLabel = getLabelForExistingNode(
|
||||||
|
value,
|
||||||
|
existingLabels,
|
||||||
|
);
|
||||||
|
setLabel(newLabel);
|
||||||
setNodes(
|
setNodes(
|
||||||
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
||||||
id,
|
id,
|
||||||
value,
|
newLabel,
|
||||||
nodes as Array<AppNode>,
|
nodes as Array<AppNode>,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
titleClassName="text-base"
|
||||||
|
inputClassName="text-base"
|
||||||
/>
|
/>
|
||||||
<span className="text-xs text-slate-400">Download Block</span>
|
<span className="text-xs text-slate-400">Download Block</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,13 +12,16 @@ import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
|||||||
import { NodeActionMenu } from "../NodeActionMenu";
|
import { NodeActionMenu } from "../NodeActionMenu";
|
||||||
import type { FileParserNode } from "./types";
|
import type { FileParserNode } from "./types";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { getUpdatedNodesAfterLabelUpdateForParameterKeys } from "../../workflowEditorUtils";
|
import {
|
||||||
|
getLabelForExistingNode,
|
||||||
|
getUpdatedNodesAfterLabelUpdateForParameterKeys,
|
||||||
|
} from "../../workflowEditorUtils";
|
||||||
import { AppNode } from "..";
|
import { AppNode } from "..";
|
||||||
|
|
||||||
function FileParserNode({ id, data }: NodeProps<FileParserNode>) {
|
function FileParserNode({ id, data }: NodeProps<FileParserNode>) {
|
||||||
const { updateNodeData, setNodes } = useReactFlow();
|
const { updateNodeData, setNodes } = useReactFlow();
|
||||||
const deleteNodeCallback = useDeleteNodeCallback();
|
const deleteNodeCallback = useDeleteNodeCallback();
|
||||||
const nodes = useNodes();
|
const nodes = useNodes<AppNode>();
|
||||||
const [label, setLabel] = useState(data.label);
|
const [label, setLabel] = useState(data.label);
|
||||||
const [inputs, setInputs] = useState({
|
const [inputs, setInputs] = useState({
|
||||||
fileUrl: data.fileUrl,
|
fileUrl: data.fileUrl,
|
||||||
@@ -49,16 +52,22 @@ function FileParserNode({ id, data }: NodeProps<FileParserNode>) {
|
|||||||
value={label}
|
value={label}
|
||||||
editable={data.editable}
|
editable={data.editable}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setLabel(value);
|
const existingLabels = nodes.map((n) => n.data.label);
|
||||||
updateNodeData(id, { label: value });
|
const newLabel = getLabelForExistingNode(
|
||||||
|
value,
|
||||||
|
existingLabels,
|
||||||
|
);
|
||||||
|
setLabel(newLabel);
|
||||||
setNodes(
|
setNodes(
|
||||||
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
||||||
id,
|
id,
|
||||||
value,
|
newLabel,
|
||||||
nodes as Array<AppNode>,
|
nodes as Array<AppNode>,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
titleClassName="text-base"
|
||||||
|
inputClassName="text-base"
|
||||||
/>
|
/>
|
||||||
<span className="text-xs text-slate-400">File Parser Block</span>
|
<span className="text-xs text-slate-400">File Parser Block</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,12 +14,15 @@ import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
|||||||
import { NodeActionMenu } from "../NodeActionMenu";
|
import { NodeActionMenu } from "../NodeActionMenu";
|
||||||
import type { LoopNode } from "./types";
|
import type { LoopNode } from "./types";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { getUpdatedNodesAfterLabelUpdateForParameterKeys } from "../../workflowEditorUtils";
|
import {
|
||||||
|
getLabelForExistingNode,
|
||||||
|
getUpdatedNodesAfterLabelUpdateForParameterKeys,
|
||||||
|
} from "../../workflowEditorUtils";
|
||||||
import { AppNode } from "..";
|
import { AppNode } from "..";
|
||||||
|
|
||||||
function LoopNode({ id, data }: NodeProps<LoopNode>) {
|
function LoopNode({ id, data }: NodeProps<LoopNode>) {
|
||||||
const { updateNodeData, setNodes } = useReactFlow();
|
const { updateNodeData, setNodes } = useReactFlow();
|
||||||
const nodes = useNodes();
|
const nodes = useNodes<AppNode>();
|
||||||
const deleteNodeCallback = useDeleteNodeCallback();
|
const deleteNodeCallback = useDeleteNodeCallback();
|
||||||
const [label, setLabel] = useState(data.label);
|
const [label, setLabel] = useState(data.label);
|
||||||
const [inputs, setInputs] = useState({
|
const [inputs, setInputs] = useState({
|
||||||
@@ -77,16 +80,22 @@ function LoopNode({ id, data }: NodeProps<LoopNode>) {
|
|||||||
value={label}
|
value={label}
|
||||||
editable={data.editable}
|
editable={data.editable}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setLabel(value);
|
const existingLabels = nodes.map((n) => n.data.label);
|
||||||
updateNodeData(id, { label: value });
|
const newLabel = getLabelForExistingNode(
|
||||||
|
value,
|
||||||
|
existingLabels,
|
||||||
|
);
|
||||||
|
setLabel(newLabel);
|
||||||
setNodes(
|
setNodes(
|
||||||
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
||||||
id,
|
id,
|
||||||
value,
|
newLabel,
|
||||||
nodes as Array<AppNode>,
|
nodes as Array<AppNode>,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
titleClassName="text-base"
|
||||||
|
inputClassName="text-base"
|
||||||
/>
|
/>
|
||||||
<span className="text-xs text-slate-400">Loop Block</span>
|
<span className="text-xs text-slate-400">Loop Block</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,12 +14,15 @@ import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
|||||||
import { NodeActionMenu } from "../NodeActionMenu";
|
import { NodeActionMenu } from "../NodeActionMenu";
|
||||||
import type { SendEmailNode } from "./types";
|
import type { SendEmailNode } from "./types";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { getUpdatedNodesAfterLabelUpdateForParameterKeys } from "../../workflowEditorUtils";
|
import {
|
||||||
|
getLabelForExistingNode,
|
||||||
|
getUpdatedNodesAfterLabelUpdateForParameterKeys,
|
||||||
|
} from "../../workflowEditorUtils";
|
||||||
import { AppNode } from "..";
|
import { AppNode } from "..";
|
||||||
|
|
||||||
function SendEmailNode({ id, data }: NodeProps<SendEmailNode>) {
|
function SendEmailNode({ id, data }: NodeProps<SendEmailNode>) {
|
||||||
const { updateNodeData, setNodes } = useReactFlow();
|
const { updateNodeData, setNodes } = useReactFlow();
|
||||||
const nodes = useNodes();
|
const nodes = useNodes<AppNode>();
|
||||||
const deleteNodeCallback = useDeleteNodeCallback();
|
const deleteNodeCallback = useDeleteNodeCallback();
|
||||||
const [label, setLabel] = useState(data.label);
|
const [label, setLabel] = useState(data.label);
|
||||||
const [inputs, setInputs] = useState({
|
const [inputs, setInputs] = useState({
|
||||||
@@ -62,16 +65,22 @@ function SendEmailNode({ id, data }: NodeProps<SendEmailNode>) {
|
|||||||
value={label}
|
value={label}
|
||||||
editable={data.editable}
|
editable={data.editable}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setLabel(value);
|
const existingLabels = nodes.map((n) => n.data.label);
|
||||||
updateNodeData(id, { label: value });
|
const newLabel = getLabelForExistingNode(
|
||||||
|
value,
|
||||||
|
existingLabels,
|
||||||
|
);
|
||||||
|
setLabel(newLabel);
|
||||||
setNodes(
|
setNodes(
|
||||||
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
||||||
id,
|
id,
|
||||||
value,
|
newLabel,
|
||||||
nodes as Array<AppNode>,
|
nodes as Array<AppNode>,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
titleClassName="text-base"
|
||||||
|
inputClassName="text-base"
|
||||||
/>
|
/>
|
||||||
<span className="text-xs text-slate-400">Send Email Block</span>
|
<span className="text-xs text-slate-400">Send Email Block</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { AppNode } from "..";
|
import { AppNode } from "..";
|
||||||
import {
|
import {
|
||||||
|
getLabelForExistingNode,
|
||||||
getOutputParameterKey,
|
getOutputParameterKey,
|
||||||
getUpdatedNodesAfterLabelUpdateForParameterKeys,
|
getUpdatedNodesAfterLabelUpdateForParameterKeys,
|
||||||
} from "../../workflowEditorUtils";
|
} from "../../workflowEditorUtils";
|
||||||
@@ -421,15 +422,22 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
value={label}
|
value={label}
|
||||||
editable={editable}
|
editable={editable}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setLabel(value);
|
const existingLabels = nodes.map((n) => n.data.label);
|
||||||
|
const newLabel = getLabelForExistingNode(
|
||||||
|
value,
|
||||||
|
existingLabels,
|
||||||
|
);
|
||||||
|
setLabel(newLabel);
|
||||||
setNodes(
|
setNodes(
|
||||||
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
||||||
id,
|
id,
|
||||||
value,
|
newLabel,
|
||||||
nodes as Array<AppNode>,
|
nodes as Array<AppNode>,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
titleClassName="text-base"
|
||||||
|
inputClassName="text-base"
|
||||||
/>
|
/>
|
||||||
<span className="text-xs text-slate-400">Task Block</span>
|
<span className="text-xs text-slate-400">Task Block</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,12 +16,15 @@ import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
|||||||
import { NodeActionMenu } from "../NodeActionMenu";
|
import { NodeActionMenu } from "../NodeActionMenu";
|
||||||
import type { TextPromptNode } from "./types";
|
import type { TextPromptNode } from "./types";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { getUpdatedNodesAfterLabelUpdateForParameterKeys } from "../../workflowEditorUtils";
|
import {
|
||||||
|
getLabelForExistingNode,
|
||||||
|
getUpdatedNodesAfterLabelUpdateForParameterKeys,
|
||||||
|
} from "../../workflowEditorUtils";
|
||||||
import { AppNode } from "..";
|
import { AppNode } from "..";
|
||||||
|
|
||||||
function TextPromptNode({ id, data }: NodeProps<TextPromptNode>) {
|
function TextPromptNode({ id, data }: NodeProps<TextPromptNode>) {
|
||||||
const { updateNodeData, setNodes } = useReactFlow();
|
const { updateNodeData, setNodes } = useReactFlow();
|
||||||
const nodes = useNodes();
|
const nodes = useNodes<AppNode>();
|
||||||
const { editable } = data;
|
const { editable } = data;
|
||||||
const deleteNodeCallback = useDeleteNodeCallback();
|
const deleteNodeCallback = useDeleteNodeCallback();
|
||||||
const [label, setLabel] = useState(data.label);
|
const [label, setLabel] = useState(data.label);
|
||||||
@@ -53,18 +56,24 @@ function TextPromptNode({ id, data }: NodeProps<TextPromptNode>) {
|
|||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<EditableNodeTitle
|
<EditableNodeTitle
|
||||||
value={label}
|
value={label}
|
||||||
editable={editable}
|
editable={data.editable}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setLabel(value);
|
const existingLabels = nodes.map((n) => n.data.label);
|
||||||
updateNodeData(id, { label: value });
|
const newLabel = getLabelForExistingNode(
|
||||||
|
value,
|
||||||
|
existingLabels,
|
||||||
|
);
|
||||||
|
setLabel(newLabel);
|
||||||
setNodes(
|
setNodes(
|
||||||
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
||||||
id,
|
id,
|
||||||
value,
|
newLabel,
|
||||||
nodes as Array<AppNode>,
|
nodes as Array<AppNode>,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
titleClassName="text-base"
|
||||||
|
inputClassName="text-base"
|
||||||
/>
|
/>
|
||||||
<span className="text-xs text-slate-400">Text Prompt Block</span>
|
<span className="text-xs text-slate-400">Text Prompt Block</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,12 +13,15 @@ import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
|||||||
import { NodeActionMenu } from "../NodeActionMenu";
|
import { NodeActionMenu } from "../NodeActionMenu";
|
||||||
import type { UploadNode } from "./types";
|
import type { UploadNode } from "./types";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { getUpdatedNodesAfterLabelUpdateForParameterKeys } from "../../workflowEditorUtils";
|
import {
|
||||||
|
getLabelForExistingNode,
|
||||||
|
getUpdatedNodesAfterLabelUpdateForParameterKeys,
|
||||||
|
} from "../../workflowEditorUtils";
|
||||||
import { AppNode } from "..";
|
import { AppNode } from "..";
|
||||||
|
|
||||||
function UploadNode({ id, data }: NodeProps<UploadNode>) {
|
function UploadNode({ id, data }: NodeProps<UploadNode>) {
|
||||||
const { updateNodeData, setNodes } = useReactFlow();
|
const { setNodes } = useReactFlow();
|
||||||
const nodes = useNodes();
|
const nodes = useNodes<AppNode>();
|
||||||
const deleteNodeCallback = useDeleteNodeCallback();
|
const deleteNodeCallback = useDeleteNodeCallback();
|
||||||
const [label, setLabel] = useState(data.label);
|
const [label, setLabel] = useState(data.label);
|
||||||
|
|
||||||
@@ -47,16 +50,22 @@ function UploadNode({ id, data }: NodeProps<UploadNode>) {
|
|||||||
value={label}
|
value={label}
|
||||||
editable={data.editable}
|
editable={data.editable}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setLabel(value);
|
const existingLabels = nodes.map((n) => n.data.label);
|
||||||
updateNodeData(id, { label: value });
|
const newLabel = getLabelForExistingNode(
|
||||||
|
value,
|
||||||
|
existingLabels,
|
||||||
|
);
|
||||||
|
setLabel(newLabel);
|
||||||
setNodes(
|
setNodes(
|
||||||
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
getUpdatedNodesAfterLabelUpdateForParameterKeys(
|
||||||
id,
|
id,
|
||||||
value,
|
newLabel,
|
||||||
nodes as Array<AppNode>,
|
nodes as Array<AppNode>,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
titleClassName="text-base"
|
||||||
|
inputClassName="text-base"
|
||||||
/>
|
/>
|
||||||
<span className="text-xs text-slate-400">Upload Block</span>
|
<span className="text-xs text-slate-400">Upload Block</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
@@ -6,66 +5,70 @@ import {
|
|||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { cn } from "@/util/utils";
|
import { cn } from "@/util/utils";
|
||||||
import { useLayoutEffect, useRef } from "react";
|
import { HorizontallyResizingInput } from "./HorizontallyResizingInput";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
value: string;
|
value: string;
|
||||||
editable: boolean;
|
editable: boolean;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
className?: string;
|
titleClassName?: string;
|
||||||
|
inputClassName?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function EditableNodeTitle({ value, editable, onChange, className }: Props) {
|
function EditableNodeTitle({
|
||||||
const ref = useRef<HTMLInputElement>(null);
|
value,
|
||||||
|
editable,
|
||||||
useLayoutEffect(() => {
|
onChange,
|
||||||
// size the textarea correctly on first render
|
titleClassName,
|
||||||
if (!ref.current) {
|
inputClassName,
|
||||||
return;
|
}: Props) {
|
||||||
}
|
const [editing, setEditing] = useState(false);
|
||||||
ref.current.style.width = `${ref.current.scrollWidth + 2}px`;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
function setSize() {
|
|
||||||
if (!ref.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ref.current.style.width = "auto";
|
|
||||||
ref.current.style.width = `${ref.current.scrollWidth + 2}px`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Input
|
{!editing ? (
|
||||||
disabled={!editable}
|
<h1
|
||||||
ref={ref}
|
className={cn("cursor-text", titleClassName)}
|
||||||
size={1}
|
onClick={() => {
|
||||||
className={cn("nopan w-min border-0 p-0", className)}
|
setEditing(true);
|
||||||
onBlur={(event) => {
|
}}
|
||||||
if (!editable) {
|
>
|
||||||
event.currentTarget.value = value;
|
{value}
|
||||||
return;
|
</h1>
|
||||||
}
|
) : (
|
||||||
onChange(event.target.value);
|
<HorizontallyResizingInput
|
||||||
}}
|
disabled={!editable}
|
||||||
onKeyDown={(event) => {
|
size={1}
|
||||||
if (!editable) {
|
autoFocus
|
||||||
return;
|
className={cn("nopan w-min border-0 p-0", inputClassName)}
|
||||||
}
|
onBlur={(event) => {
|
||||||
if (event.key === "Enter") {
|
if (!editable) {
|
||||||
event.currentTarget.blur();
|
event.currentTarget.value = value;
|
||||||
}
|
return;
|
||||||
if (event.key === "Escape") {
|
}
|
||||||
event.currentTarget.value = value;
|
if (event.currentTarget.value !== value) {
|
||||||
event.currentTarget.blur();
|
onChange(event.target.value);
|
||||||
}
|
}
|
||||||
setSize();
|
setEditing(false);
|
||||||
}}
|
}}
|
||||||
onInput={setSize}
|
onKeyDown={(event) => {
|
||||||
defaultValue={value}
|
if (!editable) {
|
||||||
/>
|
return;
|
||||||
|
}
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
event.currentTarget.blur();
|
||||||
|
}
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
event.currentTarget.value = value;
|
||||||
|
event.currentTarget.blur();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
defaultValue={value}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>Click to edit</TooltipContent>
|
<TooltipContent>Click to edit</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { useLayoutEffect, useRef } from "react";
|
||||||
|
|
||||||
|
type Props = React.ComponentProps<typeof Input>;
|
||||||
|
|
||||||
|
function HorizontallyResizingInput(props: Props) {
|
||||||
|
const ref = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
// size the textarea correctly on first render
|
||||||
|
if (!ref.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ref.current.style.width = `${ref.current.scrollWidth + 2}px`;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
function setSize() {
|
||||||
|
if (!ref.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ref.current.style.width = "auto";
|
||||||
|
ref.current.style.width = `${ref.current.scrollWidth + 2}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
size={1}
|
||||||
|
onInput={(event) => {
|
||||||
|
setSize();
|
||||||
|
props.onInput?.(event);
|
||||||
|
}}
|
||||||
|
ref={ref}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
setSize();
|
||||||
|
props.onKeyDown?.(event);
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { HorizontallyResizingInput };
|
||||||
@@ -740,6 +740,19 @@ function getAdditionalParametersForEmailBlock(
|
|||||||
return sendEmailParameters;
|
return sendEmailParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLabelForExistingNode(label: string, existingLabels: Array<string>) {
|
||||||
|
if (!existingLabels.includes(label)) {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
for (let i = 2; i < existingLabels.length + 1; i++) {
|
||||||
|
const candidate = `${label} (${i})`;
|
||||||
|
if (!existingLabels.includes(candidate)) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
createNode,
|
createNode,
|
||||||
generateNodeData,
|
generateNodeData,
|
||||||
@@ -751,4 +764,5 @@ export {
|
|||||||
getOutputParameterKey,
|
getOutputParameterKey,
|
||||||
getUpdatedNodesAfterLabelUpdateForParameterKeys,
|
getUpdatedNodesAfterLabelUpdateForParameterKeys,
|
||||||
getAdditionalParametersForEmailBlock,
|
getAdditionalParametersForEmailBlock,
|
||||||
|
getLabelForExistingNode,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user