mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-21 02:40:14 +01:00
⌨️ refactor: Favorite Item Selection & Keyboard Navigation/Focus Improvements (#10952)
* refactor: Reuse conversation switching logic from useSelectMention hook for Favorite Items - Added onSelectEndpoint prop to FavoriteItem for improved endpoint selection handling. - Refactored conversation initiation logic to utilize the new prop instead of direct navigation. - Updated FavoritesList to pass onSelectEndpoint to FavoriteItem, streamlining the interaction flow. - Replaced EndpointIcon with MinimalIcon for a cleaner UI representation of favorite models. * refactor: Enhance FavoriteItem and FavoritesList for improved accessibility and interaction - Added onRemoveFocus prop to FavoriteItem for better focus management after item removal. - Refactored event handling in FavoriteItem to support keyboard interactions for accessibility. - Updated FavoritesList to utilize the new onRemoveFocus prop, ensuring focus shifts appropriately after removing favorites. - Enhanced aria-labels and roles for better screen reader support and user experience. * refactor: Enhance EndpointModelItem for improved accessibility and interaction - Added useRef and useState hooks to manage active state and focus behavior. - Implemented MutationObserver to track changes in the data-active-item attribute for better accessibility. - Refactored favorite button handling to improve interaction and accessibility. - Updated button tabIndex based on active state to enhance keyboard navigation. * chore: Update Radix UI dependencies in package-lock and package.json files - Upgraded @radix-ui/react-alert-dialog and @radix-ui/react-dialog to version 1.1.15 across client and packages/client. - Updated related dependencies for improved compatibility and performance. - Removed outdated debug module references from package-lock.json. * refactor: Improve accessibility and interaction in conversation options - Added event handling to prevent unintended actions when renaming conversations. - Updated ConvoOptions to use Ariakit components for better accessibility and interaction. - Refactored button handlers for sharing and deleting conversations for clarity and consistency. - Enhanced dialog components with proper aria attributes and improved structure for better screen reader support. * refactor: Improve nested dialog accessibility for deleting shared link - Eliminated the setShareDialogOpen prop from both ShareButton and SharedLinkButton components to streamline the code. - Updated the delete mutation success handler in SharedLinkButton to improve focus management for accessibility. - Enhanced the OGDialog component in SharedLinkButton with a triggerRef for better interaction.
This commit is contained in:
parent
5b0cce2e2a
commit
4d7e6b4a58
12 changed files with 670 additions and 219 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import { EarthIcon, Pin, PinOff } from 'lucide-react';
|
||||
import { isAgentsEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import { useModelSelectorContext } from '../ModelSelectorContext';
|
||||
|
|
@ -18,6 +18,26 @@ export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointMod
|
|||
const { handleSelectModel } = useModelSelectorContext();
|
||||
const { isFavoriteModel, toggleFavoriteModel, isFavoriteAgent, toggleFavoriteAgent } =
|
||||
useFavorites();
|
||||
|
||||
const itemRef = useRef<HTMLDivElement>(null);
|
||||
const [isActive, setIsActive] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const element = itemRef.current;
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
setIsActive(element.hasAttribute('data-active-item'));
|
||||
});
|
||||
|
||||
observer.observe(element, { attributes: true, attributeFilter: ['data-active-item'] });
|
||||
setIsActive(element.hasAttribute('data-active-item'));
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
let isGlobal = false;
|
||||
let modelName = modelId;
|
||||
const avatarUrl = endpoint?.modelIcons?.[modelId ?? ''] || null;
|
||||
|
|
@ -42,8 +62,7 @@ export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointMod
|
|||
? isFavoriteAgent(modelId ?? '')
|
||||
: isFavoriteModel(modelId ?? '', endpoint.value);
|
||||
|
||||
const handleFavoriteClick = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
const handleFavoriteToggle = () => {
|
||||
if (!modelId) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -55,6 +74,11 @@ export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointMod
|
|||
}
|
||||
};
|
||||
|
||||
const handleFavoriteClick = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
handleFavoriteToggle();
|
||||
};
|
||||
|
||||
const renderAvatar = () => {
|
||||
const isAgentOrAssistant =
|
||||
isAgentsEndpoint(endpoint.value) || isAssistantsEndpoint(endpoint.value);
|
||||
|
|
@ -84,6 +108,7 @@ export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointMod
|
|||
|
||||
return (
|
||||
<MenuItem
|
||||
ref={itemRef}
|
||||
key={modelId}
|
||||
onClick={() => handleSelectModel(endpoint, modelId ?? '')}
|
||||
className="group flex w-full cursor-pointer items-center justify-between rounded-lg px-2 text-sm"
|
||||
|
|
@ -94,20 +119,18 @@ export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointMod
|
|||
{isGlobal && <EarthIcon className="ml-1 size-4 text-surface-submit" />}
|
||||
</div>
|
||||
<button
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
onClick={handleFavoriteClick}
|
||||
aria-label={isFavorite ? localize('com_ui_unpin') : localize('com_ui_pin')}
|
||||
className={cn(
|
||||
'rounded-md p-1 hover:bg-surface-hover',
|
||||
isFavorite ? 'visible' : 'invisible group-hover:visible',
|
||||
isFavorite ? 'visible' : 'invisible group-hover:visible group-data-[active-item]:visible',
|
||||
)}
|
||||
>
|
||||
{isFavorite ? (
|
||||
<PinOff className="h-4 w-4 text-text-secondary" />
|
||||
) : (
|
||||
<Pin
|
||||
className="h-4 w-4 text-text-secondary"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<Pin className="h-4 w-4 text-text-secondary" aria-hidden="true" />
|
||||
)}
|
||||
</button>
|
||||
{isSelected && (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue