| 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); |
| }; |
| |
| const containerCallbackRef = React.useCallback((node: HTMLDivElement | null) => { |
| setContainerNode(node); |
| }, []); |
|
|
| useEffect(() => { |
| if (containerNode && isOpen) { |
| openSharePointPicker(); |
| } |
| return () => { |
| if (!isOpen) { |
| cleanup(); |
| } |
| }; |
| |
| |
| }, [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> |
| ); |
| } |
|
|