File size: 5,428 Bytes
f0743f4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | import React, { useState, useEffect } from 'react';
import {
OGDialog,
OGDialogTitle,
OGDialogPortal,
OGDialogOverlay,
OGDialogContent,
} from '@librechat/client';
import type { SharePointBatchProgress } from '~/data-provider/Files/sharepoint';
import { useSharePointPicker, useLocalize } from '~/hooks';
interface SharePointPickerDialogProps {
isOpen: boolean;
onOpenChange: (open: boolean) => void;
onFilesSelected?: (files: any[]) => void;
disabled?: boolean;
isDownloading?: boolean;
downloadProgress?: SharePointBatchProgress | null;
maxSelectionCount?: number;
}
export default function SharePointPickerDialog({
isOpen,
onOpenChange,
onFilesSelected,
disabled = false,
isDownloading = false,
downloadProgress = null,
maxSelectionCount,
}: SharePointPickerDialogProps) {
const [containerNode, setContainerNode] = useState<HTMLDivElement | null>(null);
const localize = useLocalize();
const { openSharePointPicker, closeSharePointPicker, cleanup } = useSharePointPicker({
containerNode,
onFilesSelected,
disabled,
onClose: () => handleOpenChange(false),
maxSelectionCount,
});
const handleOpenChange = (open: boolean) => {
if (!open) {
closeSharePointPicker();
}
onOpenChange(open);
};
// Use callback ref to trigger SharePoint picker when container is attached
const containerCallbackRef = React.useCallback((node: HTMLDivElement | null) => {
setContainerNode(node);
}, []);
useEffect(() => {
if (containerNode && isOpen) {
openSharePointPicker();
}
return () => {
if (!isOpen) {
cleanup();
}
};
// we need to run this effect only when the containerNode or isOpen changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [containerNode, isOpen]);
return (
<OGDialog open={isOpen} onOpenChange={handleOpenChange}>
<OGDialogPortal>
<OGDialogOverlay className="bg-black/50" />
<OGDialogContent
className="sharepoint-picker-bg fixed left-1/2 top-1/2 z-50 h-[680px] max-h-[90vh] max-w-[90vw] -translate-x-1/2 -translate-y-1/2 rounded-lg border bg-surface-primary p-2 shadow-lg focus:outline-none"
showCloseButton={true}
>
<OGDialogTitle className="sr-only">
{localize('com_files_sharepoint_picker_title')}
</OGDialogTitle>
<div ref={containerCallbackRef} className="sharepoint-picker-bg relative flex p-2">
{/* SharePoint iframe will be injected here by the hook */}
{isDownloading && (
<div className="absolute inset-0 z-10 flex items-center justify-center rounded-lg bg-black/30 backdrop-blur-sm">
<div className="mx-4 w-full max-w-sm rounded-lg bg-surface-primary p-6 shadow-lg">
<div className="text-center">
<div className="mx-auto mb-4 h-8 w-8 animate-spin rounded-full border-b-2 border-blue-600"></div>
<h3 className="mb-2 text-lg font-semibold text-text-primary">
{localize('com_files_downloading')}
</h3>
{downloadProgress && (
<div className="space-y-2">
<p className="text-sm text-text-secondary">
{localize('com_files_download_progress', {
0: downloadProgress.completed,
1: downloadProgress.total,
})}
</p>
{downloadProgress.currentFile && (
<p className="truncate text-xs text-text-tertiary">
{downloadProgress.currentFile}
</p>
)}
<div className="h-2 w-full rounded-full bg-surface-tertiary">
<div
className="h-2 rounded-full bg-blue-600 transition-all duration-300"
style={{
width: `${Math.round((downloadProgress.completed / downloadProgress.total) * 100)}%`,
}}
></div>
</div>
<p className="text-xs text-text-tertiary">
{localize('com_files_download_percent_complete', {
0: Math.round(
(downloadProgress.completed / downloadProgress.total) * 100,
),
})}
</p>
{downloadProgress.failed.length > 0 && (
<p className="text-xs text-red-500">
{localize('com_files_download_failed', {
0: downloadProgress.failed.length,
})}
</p>
)}
</div>
)}
{!downloadProgress && (
<p className="text-sm text-text-secondary">
{localize('com_files_preparing_download')}
</p>
)}
</div>
</div>
</div>
)}
</div>
</OGDialogContent>
</OGDialogPortal>
</OGDialog>
);
}
|