Browse Source

feat: close #1762 add hover text for chat input actions

Yidadaa 1 year ago
parent
commit
88df4a2223
4 changed files with 135 additions and 41 deletions
  1. 25 0
      app/components/chat.module.scss
  2. 84 41
      app/components/chat.tsx
  3. 13 0
      app/locales/cn.ts
  4. 13 0
      app/locales/en.ts

+ 25 - 0
app/components/chat.module.scss

@@ -17,10 +17,35 @@
     transition: all ease 0.3s;
     margin-bottom: 10px;
     align-items: center;
+    height: 16px;
 
     &:not(:last-child) {
       margin-right: 5px;
     }
+
+    .text {
+      white-space: nowrap;
+      padding-left: 5px;
+      opacity: 0;
+      transform: translateX(-5px);
+      transition: all ease 0.3s;
+      transition-delay: 0.1s;
+      pointer-events: none;
+    }
+
+    &:hover {
+      .text {
+        opacity: 1;
+        transform: translate(0);
+      }
+    }
+
+    .text,
+    .icon {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
   }
 }
 

+ 84 - 41
app/components/chat.tsx

@@ -279,6 +279,52 @@ function ClearContextDivider() {
   );
 }
 
+function ChatAction(props: {
+  text: string;
+  icon: JSX.Element;
+  onClick: () => void;
+}) {
+  const iconRef = useRef<HTMLDivElement>(null);
+  const textRef = useRef<HTMLDivElement>(null);
+  const [hovering, setHovering] = useState(false);
+  const [width, setWidth] = useState(20);
+
+  const updateWidth = () => {
+    if (!iconRef.current || !textRef.current) return;
+    const getWidth = (dom: HTMLDivElement) => dom.getBoundingClientRect().width;
+    const textWidth = getWidth(textRef.current);
+    const iconWidth = getWidth(iconRef.current);
+    setWidth(hovering ? textWidth + iconWidth : iconWidth);
+  };
+
+  useEffect(() => {
+    updateWidth();
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [hovering]);
+
+  return (
+    <div
+      className={`${chatStyle["chat-input-action"]} clickable`}
+      onMouseEnter={() => setHovering(true)}
+      onMouseLeave={() => setHovering(false)}
+      style={{
+        width,
+      }}
+      onClick={() => {
+        props.onClick();
+        setTimeout(updateWidth, 1);
+      }}
+    >
+      <div ref={iconRef} className={chatStyle["icon"]}>
+        {props.icon}
+      </div>
+      <div className={chatStyle["text"]} ref={textRef}>
+        {props.text}
+      </div>
+    </div>
+  );
+}
+
 function useScrollToBottom() {
   // for auto-scroll
   const scrollRef = useRef<HTMLDivElement>(null);
@@ -330,61 +376,60 @@ export function ChatActions(props: {
   return (
     <div className={chatStyle["chat-input-actions"]}>
       {couldStop && (
-        <div
-          className={`${chatStyle["chat-input-action"]} clickable`}
+        <ChatAction
           onClick={stopAll}
-        >
-          <StopIcon />
-        </div>
+          text={Locale.Chat.InputActions.Stop}
+          icon={<StopIcon />}
+        />
       )}
       {!props.hitBottom && (
-        <div
-          className={`${chatStyle["chat-input-action"]} clickable`}
+        <ChatAction
           onClick={props.scrollToBottom}
-        >
-          <BottomIcon />
-        </div>
+          text={Locale.Chat.InputActions.ToBottom}
+          icon={<BottomIcon />}
+        />
       )}
       {props.hitBottom && (
-        <div
-          className={`${chatStyle["chat-input-action"]} clickable`}
+        <ChatAction
           onClick={props.showPromptModal}
-        >
-          <SettingsIcon />
-        </div>
+          text={Locale.Chat.InputActions.Settings}
+          icon={<SettingsIcon />}
+        />
       )}
 
-      <div
-        className={`${chatStyle["chat-input-action"]} clickable`}
+      <ChatAction
         onClick={nextTheme}
-      >
-        {theme === Theme.Auto ? (
-          <AutoIcon />
-        ) : theme === Theme.Light ? (
-          <LightIcon />
-        ) : theme === Theme.Dark ? (
-          <DarkIcon />
-        ) : null}
-      </div>
+        text={Locale.Chat.InputActions.Theme[theme]}
+        icon={
+          <>
+            {theme === Theme.Auto ? (
+              <AutoIcon />
+            ) : theme === Theme.Light ? (
+              <LightIcon />
+            ) : theme === Theme.Dark ? (
+              <DarkIcon />
+            ) : null}
+          </>
+        }
+      />
 
-      <div
-        className={`${chatStyle["chat-input-action"]} clickable`}
+      <ChatAction
         onClick={props.showPromptHints}
-      >
-        <PromptIcon />
-      </div>
+        text={Locale.Chat.InputActions.Prompt}
+        icon={<PromptIcon />}
+      />
 
-      <div
-        className={`${chatStyle["chat-input-action"]} clickable`}
+      <ChatAction
         onClick={() => {
           navigate(Path.Masks);
         }}
-      >
-        <MaskIcon />
-      </div>
+        text={Locale.Chat.InputActions.Masks}
+        icon={<MaskIcon />}
+      />
 
-      <div
-        className={`${chatStyle["chat-input-action"]} clickable`}
+      <ChatAction
+        text={Locale.Chat.InputActions.Clear}
+        icon={<BreakIcon />}
         onClick={() => {
           chatStore.updateCurrentSession((session) => {
             if (session.clearContextIndex === session.messages.length) {
@@ -395,9 +440,7 @@ export function ChatActions(props: {
             }
           });
         }}
-      >
-        <BreakIcon />
-      </div>
+      />
     </div>
   );
 }

+ 13 - 0
app/locales/cn.ts

@@ -27,6 +27,19 @@ const cn = {
       Retry: "重试",
       Delete: "删除",
     },
+    InputActions: {
+      Stop: "停止响应",
+      ToBottom: "滚到最新",
+      Theme: {
+        auto: "自动主题",
+        light: "亮色模式",
+        dark: "深色模式",
+      },
+      Prompt: "快捷指令",
+      Masks: "所有面具",
+      Clear: "清除聊天",
+      Settings: "对话设置",
+    },
     Rename: "重命名对话",
     Typing: "正在输入…",
     Input: (submitKey: string) => {

+ 13 - 0
app/locales/en.ts

@@ -28,6 +28,19 @@ const en: RequiredLocaleType = {
       Retry: "Retry",
       Delete: "Delete",
     },
+    InputActions: {
+      Stop: "Stop",
+      ToBottom: "To Latest",
+      Theme: {
+        auto: "Auto",
+        light: "Light Theme",
+        dark: "Dark Theme",
+      },
+      Prompt: "Prompts",
+      Masks: "Masks",
+      Clear: "Clear Context",
+      Settings: "Settings",
+    },
     Rename: "Rename Chat",
     Typing: "Typing…",
     Input: (submitKey: string) => {