Prevent Default on Ctrl + Backspace in ReactFlow (#706)

This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-08-14 19:42:29 +00:00 • committed by GitHub
commit 9df5e94195
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 77 additions and 8 deletions

View file

@ -1,5 +1,5 @@
import _ from "lodash"; import _ from "lodash";
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useRef, useState } from "react";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import "reactflow/dist/style.css"; import "reactflow/dist/style.css";
import "./App.css"; import "./App.css";
@ -121,6 +121,7 @@ export default function App() {
); );
}; };
return ( return (
//need parent component with width and height //need parent component with width and height
<div className="flex h-full flex-col"> <div className="flex h-full flex-col">

View file

@ -32,6 +32,13 @@ export default function AccordionComponent({
value === "" ? setValue(keyValue) : setValue(""); value === "" ? setValue(keyValue) : setValue("");
} }
const handleKeyDown = (event) => {
if (event.key === "Backspace") {
event.preventDefault();
event.stopPropagation();
}
};
return ( return (
<> <>
<Accordion <Accordion
@ -39,6 +46,7 @@ export default function AccordionComponent({
className="w-full" className="w-full"
value={value} value={value}
onValueChange={setValue} onValueChange={setValue}
onKeyDown={handleKeyDown}
> >
<AccordionItem value={keyValue} className="border-b"> <AccordionItem value={keyValue} className="border-b">
<AccordionTrigger <AccordionTrigger

View file

@ -1,6 +1,7 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { FloatComponentType } from "../../types/components"; import { FloatComponentType } from "../../types/components";
import { Input } from "../ui/input"; import { Input } from "../ui/input";
import { handleKeyDown } from "../../utils/reactflowUtils";
export default function FloatComponent({ export default function FloatComponent({
value, value,
@ -43,6 +44,9 @@ export default function FloatComponent({
onChange={(e) => { onChange={(e) => {
onChange(e.target.value); onChange(e.target.value);
}} }}
onKeyDown={(e) => {
handleKeyDown(e, value, '0');
}}
/> />
</div> </div>
); );

View file

@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import { InputComponentType } from "../../types/components"; import { InputComponentType } from "../../types/components";
import { classNames } from "../../utils/utils"; import { classNames } from "../../utils/utils";
import { Input } from "../ui/input"; import { Input } from "../ui/input";
import { handleKeyDown } from "../../utils/reactflowUtils";
export default function InputComponent({ export default function InputComponent({
value, value,
@ -19,6 +20,7 @@ export default function InputComponent({
} }
}, [disabled, onChange]); }, [disabled, onChange]);
return ( return (
<div className="relative w-full"> <div className="relative w-full">
<Input <Input
@ -34,6 +36,9 @@ export default function InputComponent({
onChange={(e) => { onChange={(e) => {
onChange(e.target.value); onChange(e.target.value);
}} }}
onKeyDown={(e) => {
handleKeyDown(e, value, '');
}}
/> />
{password && ( {password && (
<button <button

View file

@ -5,6 +5,7 @@ import _ from "lodash";
import { classNames } from "../../utils/utils"; import { classNames } from "../../utils/utils";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
import { Input } from "../ui/input"; import { Input } from "../ui/input";
import { handleKeyDown } from "../../utils/reactflowUtils";
export default function InputListComponent({ export default function InputListComponent({
value, value,
@ -39,6 +40,12 @@ export default function InputListComponent({
newInputList[idx] = e.target.value; newInputList[idx] = e.target.value;
onChange(newInputList); onChange(newInputList);
}} }}
onKeyDown={(e) => {
if (e.ctrlKey && e.key === "Backspace") {
e.preventDefault();
e.stopPropagation();
}
}}
/> />
{idx === value.length - 1 ? ( {idx === value.length - 1 ? (
<button <button

View file

@ -1,6 +1,7 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { FloatComponentType } from "../../types/components"; import { FloatComponentType } from "../../types/components";
import { Input } from "../ui/input"; import { Input } from "../ui/input";
import { handleKeyDown } from "../../utils/reactflowUtils";
export default function IntComponent({ export default function IntComponent({
value, value,
@ -17,6 +18,8 @@ export default function IntComponent({
} }
}, [disabled, onChange]); }, [disabled, onChange]);
return ( return (
<div className="w-full"> <div className="w-full">
<Input <Input
@ -37,6 +40,7 @@ export default function IntComponent({
) { ) {
event.preventDefault(); event.preventDefault();
} }
handleKeyDown(event, value, '0');
}} }}
type="number" type="number"
step="1" step="1"

View file

@ -29,6 +29,7 @@ export const INVALID_CHARACTERS = [
*/ */
export const regexHighlight = /\{([^}]+)\}/g; export const regexHighlight = /\{([^}]+)\}/g;
export const specialCharsRegex = /[!@#$%^&*()\-_=+[\]{}|;:'",.<>/?\\`´]/;
export const programmingLanguages: languageMap = { export const programmingLanguages: languageMap = {
javascript: ".js", javascript: ".js",

View file

@ -22,6 +22,7 @@ import {
varHighlightHTML, varHighlightHTML,
} from "../../utils/utils"; } from "../../utils/utils";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";
import { handleKeyDown } from "../../utils/reactflowUtils";
export default function GenericModal({ export default function GenericModal({
field_name = "", field_name = "",
@ -213,6 +214,9 @@ export default function GenericModal({
checkVariables(e.target.value); checkVariables(e.target.value);
}} }}
placeholder="Type message here." placeholder="Type message here."
onKeyDown={(e) => {
handleKeyDown(e, inputValue, '');
}}
/> />
) : type === TypeModal.PROMPT && !isEdit ? ( ) : type === TypeModal.PROMPT && !isEdit ? (
<TextAreaContentView /> <TextAreaContentView />
@ -225,6 +229,9 @@ export default function GenericModal({
setInputValue(e.target.value); setInputValue(e.target.value);
}} }}
placeholder="Type message here." placeholder="Type message here."
onKeyDown={(e) => {
handleKeyDown(e, value, '');
}}
/> />
) : ( ) : (
<></> <></>

View file

@ -1,9 +1,10 @@
import _ from "lodash"; import _ from "lodash";
import { Connection, ReactFlowInstance } from "reactflow"; import { Connection, Edge, ReactFlowInstance } from "reactflow";
import { APITemplateType } from "../types/api"; import { APITemplateType } from "../types/api";
import { FlowType, NodeType } from "../types/flow"; import { FlowType, NodeType } from "../types/flow";
import { cleanEdgesType } from "../types/utils/reactflowUtils"; import { cleanEdgesType } from "../types/utils/reactflowUtils";
import { toNormalCase } from "./utils"; import { toNormalCase } from "./utils";
import { specialCharsRegex } from "../constants/constants";
export function cleanEdges({ export function cleanEdges({
flow: { edges, nodes }, flow: { edges, nodes },
@ -232,3 +233,40 @@ export function addVersionToDuplicates(flow: FlowType, flows: FlowType[]) {
return newName; return newName;
} }
export function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>, inputValue: string | string[] | null, block: string)
{
//condition to fix bug control+backspace on Windows/Linux
if (typeof inputValue === "string" && e.ctrlKey === true && e.key === "Backspace" &&
(inputValue === block || inputValue?.charAt(inputValue?.length - 1) === ' '
|| specialCharsRegex.test(inputValue?.charAt(inputValue?.length - 1))
)) {
e.preventDefault();
e.stopPropagation();
}
//condition to fix bug control+backspace on Mac
if (typeof inputValue === "string" && e.metaKey === true && e.key === "Backspace" &&
(inputValue === block || inputValue?.charAt(inputValue?.length - 1) === ' '
|| specialCharsRegex.test(inputValue?.charAt(inputValue?.length - 1))
)) {
e.preventDefault();
e.stopPropagation();
}
if (e.ctrlKey === true && e.key === "Backspace" && inputValue === block) {
e.preventDefault();
e.stopPropagation();
}
};
export function getConnectedNodes(
edge: Edge,
nodes: Array<NodeType>
): Array<NodeType> {
const sourceId = edge.source;
const targetId = edge.target;
return nodes.filter((node) => node.id === targetId || node.id === sourceId);
}

View file

@ -65,7 +65,6 @@ import {
XCircle, XCircle,
Zap, Zap,
} from "lucide-react"; } from "lucide-react";
import { Edge, Node } from "reactflow";
import { AirbyteIcon } from "../icons/Airbyte"; import { AirbyteIcon } from "../icons/Airbyte";
import { AnthropicIcon } from "../icons/Anthropic"; import { AnthropicIcon } from "../icons/Anthropic";
import { BingIcon } from "../icons/Bing"; import { BingIcon } from "../icons/Bing";
@ -277,8 +276,3 @@ export const nodeIconsLucide = {
MessageSquare, MessageSquare,
MoreHorizontal, MoreHorizontal,
}; };
export function getConnectedNodes(edge: Edge, nodes: Array<Node>): Array<Node> {
const sourceId = edge.source;
const targetId = edge.target;
return nodes.filter((node) => node.id === targetId || node.id === sourceId);
}