Files
Dorod-Sky/skyvern-frontend/src/components/WorkflowBlockInputTextarea.tsx

113 lines
3.4 KiB
TypeScript

import { PlusIcon } from "@radix-ui/react-icons";
import { cn } from "@/util/utils";
import { AutoResizingTextarea } from "./AutoResizingTextarea/AutoResizingTextarea";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
import { WorkflowBlockParameterSelect } from "@/routes/workflows/editor/nodes/WorkflowBlockParameterSelect";
import { useWorkflowTitleStore } from "@/store/WorkflowTitleStore";
import { useEffect, useRef, useState } from "react";
import { useDebouncedCallback } from "use-debounce";
type Props = Omit<
React.ComponentProps<typeof AutoResizingTextarea>,
"onChange"
> & {
canWriteTitle?: boolean;
onChange: (value: string) => void;
nodeId: string;
};
function WorkflowBlockInputTextarea(props: Props) {
const { maybeAcceptTitle, maybeWriteTitle } = useWorkflowTitleStore();
const { nodeId, onChange, canWriteTitle = false, ...textAreaProps } = props;
const [internalValue, setInternalValue] = useState(props.value ?? "");
const textareaRef = useRef<HTMLTextAreaElement>(null);
const [cursorPosition, setCursorPosition] = useState<{
start: number;
end: number;
} | null>(null);
useEffect(() => {
setInternalValue(props.value ?? "");
}, [props.value]);
const doOnChange = useDebouncedCallback((value: string) => {
onChange(value);
if (canWriteTitle) {
maybeWriteTitle(value);
maybeAcceptTitle();
}
}, 300);
const handleTextareaSelect = () => {
if (textareaRef.current) {
setCursorPosition({
start: textareaRef.current.selectionStart,
end: textareaRef.current.selectionEnd,
});
}
};
const insertParameterAtCursor = (parameterKey: string) => {
const value = props.value ?? "";
const parameterText = `{{${parameterKey}}}`;
if (cursorPosition && textareaRef.current) {
const { start, end } = cursorPosition;
const newValue =
value.substring(0, start) + parameterText + value.substring(end);
doOnChange(newValue);
setTimeout(() => {
if (textareaRef.current) {
const newPosition = start + parameterText.length;
textareaRef.current.focus();
textareaRef.current.setSelectionRange(newPosition, newPosition);
}
}, 0);
} else {
doOnChange(`${value}${parameterText}`);
}
};
return (
<div className="relative">
<AutoResizingTextarea
{...textAreaProps}
value={internalValue}
ref={textareaRef}
onBlur={() => {
doOnChange.flush();
}}
onChange={(event) => {
setInternalValue(event.target.value);
handleTextareaSelect();
doOnChange(event.target.value);
}}
onClick={handleTextareaSelect}
onKeyUp={handleTextareaSelect}
onSelect={handleTextareaSelect}
className={cn("pr-9", props.className)}
/>
<div className="absolute right-0 top-0 flex size-9 cursor-pointer items-center justify-center">
<Popover>
<PopoverTrigger asChild>
<div className="rounded p-1 hover:bg-muted">
<PlusIcon className="size-4" />
</div>
</PopoverTrigger>
<PopoverContent className="w-[22rem]">
<WorkflowBlockParameterSelect
nodeId={nodeId}
onAdd={insertParameterAtCursor}
/>
</PopoverContent>
</Popover>
</div>
</div>
);
}
export { WorkflowBlockInputTextarea };