From 2171faa3300c654122b51b416d883d41fc9a8e97 Mon Sep 17 00:00:00 2001
From: Fu Diwei <fudiwei@sina.com>
Date: Sat, 18 Jan 2025 18:24:42 +0800
Subject: [PATCH] fix(ui): modal form input focus problem

---
 ui/src/components/MultipleInput.tsx           |  4 ++--
 .../components/workflow/node/_SharedNode.tsx  | 13 +++++++----
 ui/src/pages/workflows/WorkflowNew.tsx        | 23 ++++++++++++-------
 3 files changed, 25 insertions(+), 15 deletions(-)

diff --git a/ui/src/components/MultipleInput.tsx b/ui/src/components/MultipleInput.tsx
index f1383e34..8931379c 100644
--- a/ui/src/components/MultipleInput.tsx
+++ b/ui/src/components/MultipleInput.tsx
@@ -52,7 +52,7 @@ const MultipleInput = ({
       draft.push("");
     });
     setValue(newValue);
-    setTimeout(() => itemRefs.current[newValue.length - 1]?.focus(), 0);
+    setTimeout(() => itemRefs.current[newValue.length - 1]?.focus(), 1);
 
     onValueCreate?.(newValue.length - 1);
   };
@@ -110,7 +110,7 @@ const MultipleInput = ({
       draft.splice(index + 1, 0, "");
     });
     setValue(newValue);
-    setTimeout(() => itemRefs.current[index + 1]?.focus(), 0);
+    setTimeout(() => itemRefs.current[index + 1]?.focus(), 1);
 
     onValueCreate?.(index + 1);
   };
diff --git a/ui/src/components/workflow/node/_SharedNode.tsx b/ui/src/components/workflow/node/_SharedNode.tsx
index c6ddfe50..6ecb7da5 100644
--- a/ui/src/components/workflow/node/_SharedNode.tsx
+++ b/ui/src/components/workflow/node/_SharedNode.tsx
@@ -7,7 +7,7 @@ import {
   MoreOutlined as MoreOutlinedIcon,
 } from "@ant-design/icons";
 import { useControllableValue } from "ahooks";
-import { Button, Card, Drawer, Dropdown, Input, Modal, Popover, Space } from "antd";
+import { Button, Card, Drawer, Dropdown, Input, type InputRef, Modal, Popover, Space } from "antd";
 import { produce } from "immer";
 import { isEqual } from "radash";
 
@@ -71,9 +71,10 @@ const SharedNodeMenu = ({ trigger, node, disabled, branchId, branchIndex, afterU
 
   const [modalApi, ModelContextHolder] = Modal.useModal();
 
+  const nameInputRef = useRef<InputRef>(null);
   const nameRef = useRef<string>();
 
-  const handleRenameClick = async () => {
+  const handleRenameConfirm = async () => {
     const oldName = node.name;
     const newName = nameRef.current?.trim()?.substring(0, 64) || oldName;
     if (oldName === newName) {
@@ -125,11 +126,12 @@ const SharedNodeMenu = ({ trigger, node, disabled, branchId, branchIndex, afterU
                   content: (
                     <div className="pb-2 pt-4">
                       <Input
-                        ref={(ref) => setTimeout(() => ref?.focus({ cursor: "end" }), 0)}
+                        ref={nameInputRef}
+                        autoFocus
                         defaultValue={node.name}
                         onChange={(e) => (nameRef.current = e.target.value)}
                         onPressEnter={async () => {
-                          await handleRenameClick();
+                          await handleRenameConfirm();
                           dialog.destroy();
                         }}
                       />
@@ -137,8 +139,9 @@ const SharedNodeMenu = ({ trigger, node, disabled, branchId, branchIndex, afterU
                   ),
                   icon: null,
                   okText: t("common.button.save"),
-                  onOk: handleRenameClick,
+                  onOk: handleRenameConfirm,
                 });
+                setTimeout(() => nameInputRef.current?.focus(), 1);
               },
             },
             {
diff --git a/ui/src/pages/workflows/WorkflowNew.tsx b/ui/src/pages/workflows/WorkflowNew.tsx
index 8665f055..f24dde4c 100644
--- a/ui/src/pages/workflows/WorkflowNew.tsx
+++ b/ui/src/pages/workflows/WorkflowNew.tsx
@@ -1,8 +1,8 @@
-import { useState } from "react";
+import { useEffect, useRef, useState } from "react";
 import { useTranslation } from "react-i18next";
 import { useNavigate } from "react-router-dom";
 import { PageHeader } from "@ant-design/pro-components";
-import { Card, Col, Form, Input, Row, Spin, Typography, notification } from "antd";
+import { Card, Col, Form, Input, type InputRef, Row, Spin, Typography, notification } from "antd";
 import { createSchemaFieldRule } from "antd-zod";
 import { z } from "zod";
 
@@ -81,6 +81,17 @@ const WorkflowNew = () => {
   });
   const [formModalOpen, setFormModalOpen] = useState(false);
 
+  useEffect(() => {
+    if (formModalOpen) {
+      setTimeout(() => inputRef.current?.focus({ cursor: "end" }), 1);
+    } else {
+      setTemplateSelectKey(undefined);
+      formInst.resetFields();
+    }
+  }, [formModalOpen]);
+
+  const inputRef = useRef<InputRef>(null);
+
   const handleTemplateClick = (key: TemplateKeys) => {
     setTemplateSelectKey(key);
     setFormModalOpen(true);
@@ -88,11 +99,6 @@ const WorkflowNew = () => {
 
   const handleModalOpenChange = (open: boolean) => {
     setFormModalOpen(open);
-
-    if (!open) {
-      setTemplateSelectKey(undefined);
-      formInst.resetFields();
-    }
   };
 
   const handleModalFormFinish = () => {
@@ -155,6 +161,7 @@ const WorkflowNew = () => {
 
         <ModalForm
           {...formProps}
+          autoFocus
           disabled={formPending}
           layout="vertical"
           form={formInst}
@@ -167,7 +174,7 @@ const WorkflowNew = () => {
           onOpenChange={handleModalOpenChange}
         >
           <Form.Item name="name" label={t("workflow.new.modal.form.name.label")} rules={[formRule]}>
-            <Input ref={(ref) => setTimeout(() => ref?.focus({ cursor: "end" }), 0)} placeholder={t("workflow.new.modal.form.name.placeholder")} />
+            <Input ref={inputRef} autoFocus placeholder={t("workflow.new.modal.form.name.placeholder")} />
           </Form.Item>
 
           <Form.Item name="description" label={t("workflow.new.modal.form.description.label")} rules={[formRule]}>