(feat:docs) adding view option

This commit is contained in:
ManishMadan2882
2025-03-05 03:12:48 +05:30
parent 2b7f4de832
commit 377670b34a
3 changed files with 112 additions and 39 deletions

View File

@@ -0,0 +1,11 @@
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5819_12451)">
<path d="M8.40039 14.3C5.06706 14.3 2.53372 12.4334 0.533724 8.63338L0.400391 8.30005L0.533724 7.96672C2.53372 4.16672 5.06706 2.30005 8.40039 2.30005C11.7337 2.30005 14.2671 4.16672 16.2671 7.96672L16.4004 8.30005L16.2671 8.63338C14.2671 12.4334 11.7337 14.3 8.40039 14.3ZM1.93372 8.30005C3.60039 11.4334 5.73372 12.9667 8.40039 12.9667C11.0671 12.9667 13.2004 11.4334 14.8671 8.30005C13.2004 5.16672 11.0671 3.63338 8.40039 3.63338C5.73372 3.63338 3.60039 5.16672 1.93372 8.30005Z" fill="#747474"/>
<path d="M8.40072 11.6333C6.53405 11.6333 5.06738 10.1667 5.06738 8.30001C5.06738 6.43334 6.53405 4.96667 8.40072 4.96667C10.2674 4.96667 11.734 6.43334 11.734 8.30001C11.734 10.1667 10.2674 11.6333 8.40072 11.6333ZM8.40072 6.30001C7.26738 6.30001 6.40072 7.16667 6.40072 8.30001C6.40072 9.43334 7.26738 10.3 8.40072 10.3C9.53405 10.3 10.4007 9.43334 10.4007 8.30001C10.4007 7.16667 9.53405 6.30001 8.40072 6.30001Z" fill="#747474"/>
</g>
<defs>
<clipPath id="clip0_5819_12451">
<rect width="16" height="16" fill="white" transform="translate(0.400391 0.300049)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,4 +1,5 @@
import React from 'react';
import ReactDOM from 'react-dom';
type DropdownMenuProps = {
name: string;
@@ -10,7 +11,9 @@ type DropdownMenuProps = {
onOpenChange?: (isOpen: boolean) => void;
anchorRef?: React.RefObject<HTMLElement>;
className?: string;
position?: 'left' | 'right';
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
offset?: { x: number; y: number };
contextMenuAdjacent?: boolean; // New prop to indicate if it should position next to context menu
};
export default function DropdownMenu({
@@ -22,8 +25,10 @@ export default function DropdownMenu({
isOpen: controlledIsOpen,
onOpenChange,
anchorRef,
className,
position = 'left',
className = '',
position = 'bottom-right',
offset = { x: 0, y: 8 },
contextMenuAdjacent = false, // Default to false for backward compatibility
}: DropdownMenuProps) {
const dropdownRef = React.useRef<HTMLDivElement>(null);
const [internalIsOpen, setInternalIsOpen] = React.useState(false);
@@ -38,7 +43,8 @@ export default function DropdownMenu({
const handleClickOutside = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
!dropdownRef.current.contains(event.target as Node) &&
!anchorRef?.current?.contains(event.target as Node)
) {
setIsOpen(false);
}
@@ -51,20 +57,62 @@ export default function DropdownMenu({
};
React.useEffect(() => {
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
if (isOpen) {
document.addEventListener('mousedown', handleClickOutside);
return () =>
document.removeEventListener('mousedown', handleClickOutside);
}
}, [isOpen]);
return (
<div className={`fixed ${className || ''}`} ref={dropdownRef}>
if (!isOpen) return null;
const getMenuPosition = (): React.CSSProperties => {
if (!anchorRef?.current) return {};
const rect = anchorRef.current.getBoundingClientRect();
// Default positioning
let top = rect.bottom + offset.y;
let left = rect.right + offset.x;
if (contextMenuAdjacent) {
// Position to the left of the context menu
left = rect.left - 50; // Width of dropdown + some spacing
top = rect.top; // Align tops
} else {
// Standard positioning based on position prop
switch (position) {
case 'bottom-left':
left = rect.left - offset.x;
break;
case 'top-right':
top = rect.top - offset.y;
break;
case 'top-left':
top = rect.top - offset.y;
left = rect.left - offset.x;
break;
// bottom-right is default
}
}
return {
position: 'fixed',
top: `${top}px`,
left: `${left}px`,
zIndex: 9999,
};
};
// Use a portal to render the dropdown outside the table flow
return ReactDOM.createPortal(
<div
ref={dropdownRef}
style={{ ...getMenuPosition() }}
onClick={(e) => e.stopPropagation()}
>
<div
className={`w-28 transform rounded-md bg-white dark:bg-dark-charcoal shadow-lg ring-1 ring-black ring-opacity-5 transition-all duration-200 ease-in-out ${
isOpen
? 'scale-100 opacity-100'
: 'pointer-events-none scale-95 opacity-0'
}`}
className={`w-28 transform rounded-md bg-white dark:bg-dark-charcoal shadow-lg ring-1 ring-black ring-opacity-5 transition-all duration-200 ease-in-out ${className}`}
>
<div
role="menu"
@@ -89,6 +137,7 @@ export default function DropdownMenu({
))}
</div>
</div>
</div>
</div>,
document.body,
);
}

View File

@@ -6,6 +6,7 @@ import userService from '../api/services/userService';
import ArrowLeft from '../assets/arrow-left.svg';
import caretSort from '../assets/caret-sort.svg';
import Edit from '../assets/edit.svg';
import EyeView from '../assets/eye-view.svg';
import NoFilesDarkIcon from '../assets/no-files-dark.svg';
import NoFilesIcon from '../assets/no-files.svg';
import SyncIcon from '../assets/sync.svg';
@@ -81,13 +82,19 @@ export default function Documents({
e.preventDefault();
e.stopPropagation();
// Close any open menu if clicking on a different button
if (activeMenuId && activeMenuId !== docId) {
const isAnyMenuOpen =
(syncMenuState.isOpen && syncMenuState.docId === docId) ||
activeMenuId === docId;
if (isAnyMenuOpen) {
// Close both menus
setSyncMenuState((prev) => ({ ...prev, isOpen: false, docId: null }));
setActiveMenuId(null);
return;
}
// Toggle the clicked menu
setActiveMenuId((prev) => (prev === docId ? null : docId));
// If no menu is open, open the context menu
setActiveMenuId(docId);
};
// Close menu when clicking outside
@@ -223,6 +230,16 @@ export default function Documents({
const getActionOptions = (index: number, document: Doc): MenuOption[] => {
const actions: MenuOption[] = [
{
icon: EyeView,
label: t('settings.documents.view'),
onClick: () => {
setShowDocumentChunks(document);
},
iconWidth: 18,
iconHeight: 18,
variant: 'primary',
},
{
icon: Trash,
label: t('convTile.delete'),
@@ -312,8 +329,8 @@ export default function Documents({
<th className="py-3 px-4 text-left text-xs font-medium text-sonic-silver uppercase w-[45%]">
{t('settings.documents.name')}
</th>
<th className="py-3 px-4 text-center text-xs font-medium text-sonic-silver uppercase w-[20%]">
<div className="flex justify-center items-center">
<th className="py-3 px-4 text-xs font-medium text-sonic-silver uppercase w-[30%]">
<div className="flex justify-start items-center">
{t('settings.documents.date')}
<img
className="cursor-pointer ml-2"
@@ -323,8 +340,8 @@ export default function Documents({
/>
</div>
</th>
<th className="py-3 px-4 text-center text-xs font-medium text-sonic-silver uppercase w-[25%]">
<div className="flex justify-center items-center">
<th className="py-3 px-4 text-xs font-medium text-sonic-silver uppercase w-[15%]">
<div className="flex justify-start items-center">
<span className="hidden sm:inline">
{t('settings.documents.tokenUsage')}
</span>
@@ -339,10 +356,8 @@ export default function Documents({
/>
</div>
</th>
<th className="py-3 px-4 text-right text-xs font-medium text-gray-700 dark:text-[#E0E0E0] uppercase w-[10%]">
<span className="sr-only">
{t('settings.documents.actions')}
</span>
<th className="py-3 px-4 sr-only w-[10%]">
{t('settings.documents.actions')}
</th>
</tr>
</thead>
@@ -363,27 +378,23 @@ export default function Documents({
const docId = document.id ? document.id.toString() : '';
return (
<tr
key={docId}
className="group transition-colors cursor-pointer"
onClick={() => setShowDocumentChunks(document)}
>
<tr key={docId} className="group transition-colors">
<td
className="py-4 px-4 text-sm text-gray-700 dark:text-[#E0E0E0] w-[45%] min-w-48 max-w-0 truncate group-hover:bg-gray-50 dark:group-hover:bg-gray-800/50"
className="py-4 px-4 text-sm text-gray-700 dark:text-[#E0E0E0] min-w-48 max-w-0 truncate group-hover:bg-gray-50 dark:group-hover:bg-gray-800/50"
title={document.name}
>
{document.name}
</td>
<td className="py-4 px-4 text-center text-sm text-gray-700 dark:text-[#E0E0E0] whitespace-nowrap w-[20%] group-hover:bg-gray-50 dark:group-hover:bg-gray-800/50">
<td className="py-4 px-4 text-sm text-gray-700 dark:text-[#E0E0E0] whitespace-nowrap group-hover:bg-gray-50 dark:group-hover:bg-gray-800/50">
{document.date ? formatDate(document.date) : ''}
</td>
<td className="py-4 px-4 text-center text-sm text-gray-700 dark:text-[#E0E0E0] whitespace-nowrap w-[25%] group-hover:bg-gray-50 dark:group-hover:bg-gray-800/50">
<td className="py-4 px-4 text-sm text-gray-700 dark:text-[#E0E0E0] whitespace-nowrap group-hover:bg-gray-50 dark:group-hover:bg-gray-800/50">
{document.tokens
? formatTokens(+document.tokens)
: ''}
</td>
<td
className="py-4 px-4 text-right w-[10%] group-hover:bg-gray-50 dark:group-hover:bg-gray-800/50"
className="py-4 px-4 text-right group-hover:bg-gray-50 dark:group-hover:bg-gray-800/50"
onClick={(e) => e.stopPropagation()}
>
<div
@@ -412,7 +423,9 @@ export default function Documents({
}));
}}
anchorRef={getMenuRef(docId)}
className="absolute right-12 top-0"
position="bottom-left"
offset={{ x: 24, y: -24 }}
className="min-w-[120px]"
/>
)}
<button