From 69d2bb68b4b202a935330ff64f767cee045fea5d Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Wed, 14 Aug 2024 00:22:20 -0400 Subject: [PATCH] Remove react-text-truncate and use custom 'see more' button in task header --- webview-ui/package-lock.json | 14 ---- webview-ui/package.json | 1 - webview-ui/src/components/TaskHeader.tsx | 102 +++++++++++++++-------- 3 files changed, 67 insertions(+), 50 deletions(-) diff --git a/webview-ui/package-lock.json b/webview-ui/package-lock.json index e5f12c3..e74837e 100644 --- a/webview-ui/package-lock.json +++ b/webview-ui/package-lock.json @@ -21,7 +21,6 @@ "react-markdown": "^9.0.1", "react-scripts": "5.0.1", "react-syntax-highlighter": "^15.5.0", - "react-text-truncate": "^0.19.0", "react-textarea-autosize": "^8.5.3", "react-use": "^17.5.1", "react-virtuoso": "^4.7.13", @@ -17453,19 +17452,6 @@ "react": ">= 0.14.0" } }, - "node_modules/react-text-truncate": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/react-text-truncate/-/react-text-truncate-0.19.0.tgz", - "integrity": "sha512-QxHpZABfGG0Z3WEYbRTZ+rXdZn50Zvp+sWZXgVAd7FCKAMzv/kcwctTpNmWgXDTpAoHhMjOVwmgRtX3x5yeF4w==", - "license": "MIT", - "dependencies": { - "prop-types": "^15.5.7" - }, - "peerDependencies": { - "react": "^15.4.1 || ^16.0.0 || ^17.0.0 || || ^18.0.0", - "react-dom": "^15.4.1 || ^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/react-textarea-autosize": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz", diff --git a/webview-ui/package.json b/webview-ui/package.json index 3b9733b..65c0e10 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -16,7 +16,6 @@ "react-markdown": "^9.0.1", "react-scripts": "5.0.1", "react-syntax-highlighter": "^15.5.0", - "react-text-truncate": "^0.19.0", "react-textarea-autosize": "^8.5.3", "react-use": "^17.5.1", "react-virtuoso": "^4.7.13", diff --git a/webview-ui/src/components/TaskHeader.tsx b/webview-ui/src/components/TaskHeader.tsx index af910b7..7a71e40 100644 --- a/webview-ui/src/components/TaskHeader.tsx +++ b/webview-ui/src/components/TaskHeader.tsx @@ -1,6 +1,5 @@ import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" import React, { useEffect, useRef, useState } from "react" -import TextTruncate from "react-text-truncate" import { useWindowSize } from "react-use" import { ClaudeMessage } from "../../../src/shared/ExtensionMessage" import { vscode } from "../utils/vscode" @@ -17,8 +16,9 @@ interface TaskHeaderProps { const TaskHeader: React.FC = ({ task, tokensIn, tokensOut, totalCost, onClose, isHidden }) => { const [isExpanded, setIsExpanded] = useState(false) - const [textTruncateKey, setTextTruncateKey] = useState(0) + const [showSeeMore, setShowSeeMore] = useState(false) const textContainerRef = useRef(null) + const textRef = useRef(null) /* When dealing with event listeners in React components that depend on state variables, we face a challenge. We want our listener to always use the most up-to-date version of a callback function that relies on current state, but we don't want to constantly add and remove event listeners as that function updates. This scenario often arises with resize listeners or other window events. Simply adding the listener in a useEffect with an empty dependency array risks using stale state, while including the callback in the dependencies can lead to unnecessary re-registrations of the listener. There are react hook libraries that provide a elegant solution to this problem by utilizing the useRef hook to maintain a reference to the latest callback function without triggering re-renders or effect re-runs. This approach ensures that our event listener always has access to the most current state while minimizing performance overhead and potential memory leaks from multiple listener registrations. @@ -52,25 +52,29 @@ const TaskHeader: React.FC = ({ task, tokensIn, tokensOut, tota After: */ - const { height: windowHeight } = useWindowSize() + const { height: windowHeight, width: windowWidth } = useWindowSize() useEffect(() => { if (isExpanded && textContainerRef.current) { - const maxHeight = windowHeight * (3 / 5) + const maxHeight = windowHeight * (1 / 2) textContainerRef.current.style.maxHeight = `${maxHeight}px` } }, [isExpanded, windowHeight]) useEffect(() => { - if (!isHidden) { - /* - There's an issue with TextTruncate where it is hidden when removed from the screen. It uses canvas to measure text width and adjusts the content accordingly. However, when the component is hidden, the canvas is not rendered and the text is not measured. - We can fix this by forcing re-render after navigation by using a key prop that changes when you navigate back to the page. - - https://github.com/ShinyChang/React-Text-Truncate?tab=readme-ov-file#faq - */ - setTextTruncateKey((prev) => prev + 1) + if (textRef.current && textContainerRef.current) { + let textContainerHeight = textContainerRef.current.clientHeight + if (!textContainerHeight) { + textContainerHeight = textContainerRef.current.getBoundingClientRect().height + } + const isOverflowing = textRef.current.scrollHeight > textContainerHeight + // necessary to show see more button again if user resizes window to expand and then back to collapse + if (!isOverflowing) { + setIsExpanded(false) + } + setShowSeeMore(isOverflowing) } - }, [isHidden]) + }, [task.text, windowWidth]) const toggleExpand = () => setIsExpanded(!isExpanded) @@ -112,40 +116,68 @@ const TaskHeader: React.FC = ({ task, tokensIn, tokensOut, tota overflowY: isExpanded ? "auto" : "hidden", wordBreak: "break-word", overflowWrap: "anywhere", + position: "relative", }}> - + {task.text} + + {!isExpanded && showSeeMore && ( +
+
+
See more - - } - /> - {isExpanded && ( - - See less - +
+
)}
+ {isExpanded && showSeeMore && ( +
+ See less +
+ )} {task.images && task.images.length > 0 && }
-
+
Tokens: