From 6dac50a3fc0b5dddc3da5e91bae8bbc7000538e4 Mon Sep 17 00:00:00 2001 From: Deon Sanchez <69873175+deon-sanchez@users.noreply.github.com> Date: Tue, 1 Apr 2025 12:28:06 -0600 Subject: [PATCH] =?UTF-8?q?feat:=20composio=20clean=20up=20=F0=9F=A7=B9=20?= =?UTF-8?q?(#7371)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: enhance animations and improve ListSelectionComponent functionality - Added overlay and content animations to tailwind.config.mjs for smoother transitions. - Updated ListSelectionComponent to reset search input when the dialog opens and improved item display styling. - Refactored ConnectionComponent to utilize the new onSelection prop in ListSelectionComponent for better selection handling. * fix: refine animation timings and enhance ListSelectionComponent layout - Adjusted animation durations in tailwind.config.mjs for smoother transitions. - Updated ListSelectionComponent to increase maximum height for improved content display. * feat: enhance ListSelectionComponent with hover functionality and keyboard navigation - Added mouse enter and leave handlers to ListItem for improved user interaction. - Implemented keyboard navigation to select items using the Enter key when an item is hovered. - Updated styling for ListItem to enhance visual feedback during hover states. * feat: enhance ListItem with hover state and selection feedback - Added hover state management to ListItem, allowing visual feedback when an item is hovered. - Updated the rendering logic to display a "Select" label and an icon when the item is hovered. - Improved the selection indicator for better user experience. * feat: enhance ListSelectionComponent with keyboard navigation and focus management - Added keyboard navigation to allow users to select items using the Arrow keys and Enter key. - Implemented focus management to highlight the currently focused item in the list. - Updated the component to reset focus and search input when the dialog opens or the filtered list changes. - Improved item rendering to visually indicate focus state. * feat: enhance ListSelectionComponent with improved keyboard navigation and hover management - Added state management for keyboard navigation to improve user experience when selecting items. - Implemented hover state clearing during keyboard navigation to prevent visual conflicts. - Updated ListItem to conditionally apply hover styles based on keyboard navigation state. - Enhanced focus management to reset when the filtered list changes or when the dialog opens. * fix: refine animation timings and update ListSelectionComponent layout - Adjusted clipPath values in tailwind.config.mjs for improved overlay animations. - Increased animation durations for overlay and content transitions to enhance smoothness. - Removed unnecessary padding in ListSelectionComponent for a cleaner layout. * refactor: update ListSelectionComponent styles for improved visual consistency - Changed text color and background color classes in ListItem to enhance readability and maintain design consistency. - Updated icon color to align with the new text styling for better visual integration. * [autofix.ci] apply automated fixes * fix: improve ListSelectionComponent behavior and cleanup event listeners * fix: adjust minimum height of ListSelectionComponent dialog for better usability --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/frontend/package-lock.json | 1 + .../ListSelectionComponent/index.tsx | 234 +++++++++++++++--- .../components/connectionComponent/index.tsx | 2 +- .../components/ui/dialog-with-no-close.tsx | 15 +- src/frontend/tailwind.config.mjs | 46 ++++ 5 files changed, 246 insertions(+), 52 deletions(-) diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 47a71b295..70662d9f2 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -709,6 +709,7 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", + "extraneous": true, "inBundle": true, "license": "MIT", "engines": { diff --git a/src/frontend/src/CustomNodes/GenericNode/components/ListSelectionComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/ListSelectionComponent/index.tsx index 0d8bd5ea3..989a058aa 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/ListSelectionComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/ListSelectionComponent/index.tsx @@ -1,15 +1,16 @@ import ForwardedIconComponent from "@/components/common/genericIconComponent"; +import ShadTooltip from "@/components/common/shadTooltipComponent"; import SearchBarComponent from "@/components/core/parameterRenderComponent/components/searchBarComponent"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent } from "@/components/ui/dialog-with-no-close"; import { cn } from "@/utils/utils"; -import { useCallback, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; // Update interface with better types interface ListSelectionComponentProps { open: boolean; - options: any[]; onClose: () => void; + options: any[]; setSelectedList: (action: any[]) => void; selectedList: any[]; searchCategories?: string[]; @@ -22,41 +23,99 @@ const ListItem = ({ isSelected, onClick, className, + onMouseEnter, + onMouseLeave, + isFocused, + isKeyboardNavActive, }: { item: any; isSelected: boolean; onClick: () => void; className?: string; -}) => ( - -); + onClick={onClick} + onMouseEnter={() => { + if (!isKeyboardNavActive) { + setIsHovered(true); + onMouseEnter(); + } + }} + onMouseLeave={() => { + setIsHovered(false); + onMouseLeave(); + }} + // Disable pointer events during keyboard navigation + style={{ pointerEvents: isKeyboardNavActive ? "none" : "auto" }} + > +
+ {item.icon && ( + + )} +
{item.name}
+ {"metaData" in item && item.metaData && ( +
{item.metaData}
+ )} + {isHovered || isFocused ? ( +
+
+ Select +
+
+ +
+
+ ) : ( + // Always show the check icon when selected, regardless of hover/focus state + isSelected && ( + + ) + )} +
+ + ); +}; const ListSelectionComponent = ({ open, @@ -69,6 +128,10 @@ const ListSelectionComponent = ({ limit = 1, }: ListSelectionComponentProps) => { const [search, setSearch] = useState(""); + const [hoveredItem, setHoveredItem] = useState(null); + const [focusedIndex, setFocusedIndex] = useState(-1); + const [isKeyboardNavActive, setIsKeyboardNavActive] = useState(false); + const listContainerRef = useRef(null); const filteredList = useMemo(() => { if (!search.trim()) { @@ -110,19 +173,96 @@ const ListSelectionComponent = ({ }, ]); onClose(); - setSearch(""); } }, - [selectedList, setSelectedList, onClose, limit], + [selectedList, setSelectedList, limit, onClose], ); - const handleCloseDialog = useCallback(() => { - onClose(); - }, [onClose]); + // Reset focus state when filtered list changes + useEffect(() => { + if (open) { + if (filteredList.length > 0) { + setFocusedIndex(0); + setHoveredItem(filteredList[0]); + } else { + setFocusedIndex(-1); + setHoveredItem(null); + } + } + }, [open, filteredList.length]); + + // Reset search when dialog opens + useEffect(() => { + if (open) { + setSearch(""); + setIsKeyboardNavActive(false); + } + }, [open]); + + // Handle keyboard navigation + const handleKeyDown = useCallback( + (e: React.KeyboardEvent) => { + if (filteredList.length === 0) return; + + switch (e.key) { + case "ArrowDown": + e.preventDefault(); + setIsKeyboardNavActive(true); + setFocusedIndex((prev) => { + const newIndex = prev < filteredList.length - 1 ? prev + 1 : 0; + setHoveredItem(filteredList[newIndex]); + return newIndex; + }); + break; + case "ArrowUp": + e.preventDefault(); + setIsKeyboardNavActive(true); + setFocusedIndex((prev) => { + const newIndex = prev > 0 ? prev - 1 : filteredList.length - 1; + setHoveredItem(filteredList[newIndex]); + return newIndex; + }); + break; + case "Enter": + if (hoveredItem) { + e.preventDefault(); + handleSelectAction(hoveredItem); + if (onSelection) { + onSelection(hoveredItem); + } + } + break; + case "Escape": + e.preventDefault(); + onClose(); + break; + } + }, + [filteredList, hoveredItem, handleSelectAction, onSelection, onClose], + ); + + // Detect mouse movement to switch from keyboard to mouse navigation + useEffect(() => { + const handleMouseMove = () => { + if (isKeyboardNavActive) { + setIsKeyboardNavActive(false); + } + }; + + if (open) { + window.addEventListener("mousemove", handleMouseMove); + return () => { + window.removeEventListener("mousemove", handleMouseMove); + }; + } + }, [open, isKeyboardNavActive]); return ( - - + !isOpen && onClose()}> +
-
+
{filteredList.length > 0 ? ( filteredList.map((item, index) => ( { + setHoveredItem(item); + setFocusedIndex(index); + setIsKeyboardNavActive(false); + }} + onMouseLeave={() => { + setHoveredItem(null); + // Don't reset focused index on mouse leave + }} + isFocused={focusedIndex === index} + isKeyboardNavActive={isKeyboardNavActive} /> )) ) : ( diff --git a/src/frontend/src/components/core/parameterRenderComponent/components/connectionComponent/index.tsx b/src/frontend/src/components/core/parameterRenderComponent/components/connectionComponent/index.tsx index a7fda4c72..1d5a2fc31 100644 --- a/src/frontend/src/components/core/parameterRenderComponent/components/connectionComponent/index.tsx +++ b/src/frontend/src/components/core/parameterRenderComponent/components/connectionComponent/index.tsx @@ -230,8 +230,8 @@ const ConnectionComponent = ({ ( - -
- {children} -
-
+const DialogPortal = ({ ...props }: DialogPrimitive.DialogPortalProps) => ( + ); DialogPortal.displayName = DialogPrimitive.Portal.displayName; @@ -25,7 +18,7 @@ const DialogOverlay = React.forwardRef<