mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-02 08:38:51 +01:00
📁 feat: Integrate SharePoint File Picker and Download Workflow (#8651)
* feat(sharepoint): integrate SharePoint file picker and download workflow Introduces end‑to‑end SharePoint import support: * Token exchange with Microsoft Graph and scope management (`useSharePointToken`) * Re‑usable hooks: `useSharePointPicker`, `useSharePointDownload`, `useSharePointFileHandling` * FileSearch dropdown now offers **From Local Machine** / **From SharePoint** sources and gracefully falls back when SharePoint is disabled * Agent upload model, `AttachFileMenu`, and `DropdownPopup` extended for SharePoint files and sub‑menus * Blurry overlay with progress indicator and `maxSelectionCount` limit during downloads * Cache‑flush utility (`config/flush-cache.js`) supporting Redis & filesystem, with dry‑run and npm script * Updated `SharePointIcon` (uses `currentColor`) and new i18n keys * Bug fixes: placeholder syntax in progress message, picker event‑listener cleanup * Misc style and performance optimizations * Fix ESLint warnings --------- Co-authored-by: Atef Bellaaj <slalom.bellaaj@external.daimlertruck.com>
This commit is contained in:
parent
47caafa8f8
commit
d786b1b419
40 changed files with 2500 additions and 123 deletions
|
|
@ -1,4 +1,6 @@
|
|||
import { useState, useRef } from 'react';
|
||||
import { Folder } from 'lucide-react';
|
||||
import * as Ariakit from '@ariakit/react';
|
||||
import {
|
||||
EModelEndpoint,
|
||||
EToolResources,
|
||||
|
|
@ -7,16 +9,21 @@ import {
|
|||
} from 'librechat-data-provider';
|
||||
import {
|
||||
HoverCard,
|
||||
HoverCardContent,
|
||||
HoverCardPortal,
|
||||
HoverCardTrigger,
|
||||
DropdownPopup,
|
||||
AttachmentIcon,
|
||||
CircleHelpIcon,
|
||||
AttachmentIcon,
|
||||
CircleHelpIcon,
|
||||
SharePointIcon,
|
||||
HoverCardPortal,
|
||||
HoverCardContent,
|
||||
HoverCardTrigger,
|
||||
} from '@librechat/client';
|
||||
import type { ExtendedFile } from '~/common';
|
||||
import { useFileHandling, useLocalize, useLazyEffect } from '~/hooks';
|
||||
import { useFileHandling, useLocalize, useLazyEffect, useSharePointFileHandling } from '~/hooks';
|
||||
import { useGetFileConfig, useGetStartupConfig } from '~/data-provider';
|
||||
import { SharePointPickerDialog } from '~/components/SharePoint';
|
||||
import FileRow from '~/components/Chat/Input/Files/FileRow';
|
||||
import { useGetFileConfig } from '~/data-provider';
|
||||
import { useChatContext } from '~/Providers';
|
||||
import { ESide } from '~/common';
|
||||
|
||||
|
|
@ -31,6 +38,10 @@ export default function FileContext({
|
|||
const { setFilesLoading } = useChatContext();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [files, setFiles] = useState<Map<string, ExtendedFile>>(new Map());
|
||||
const [isPopoverActive, setIsPopoverActive] = useState(false);
|
||||
const [isSharePointDialogOpen, setIsSharePointDialogOpen] = useState(false);
|
||||
const { data: startupConfig } = useGetStartupConfig();
|
||||
const sharePointEnabled = startupConfig?.sharePointFilePickerEnabled;
|
||||
|
||||
const { data: fileConfig = defaultFileConfig } = useGetFileConfig({
|
||||
select: (data) => mergeFileConfig(data),
|
||||
|
|
@ -41,7 +52,11 @@ export default function FileContext({
|
|||
additionalMetadata: { agent_id, tool_resource: EToolResources.ocr },
|
||||
fileSetter: setFiles,
|
||||
});
|
||||
|
||||
const { handleSharePointFiles, isProcessing, downloadProgress } = useSharePointFileHandling({
|
||||
overrideEndpoint: EModelEndpoint.agents,
|
||||
additionalMetadata: { agent_id, tool_resource: EToolResources.file_search },
|
||||
fileSetter: setFiles,
|
||||
});
|
||||
useLazyEffect(
|
||||
() => {
|
||||
if (_files) {
|
||||
|
|
@ -54,19 +69,45 @@ export default function FileContext({
|
|||
|
||||
const endpointFileConfig = fileConfig.endpoints[EModelEndpoint.agents];
|
||||
const isUploadDisabled = endpointFileConfig.disabled ?? false;
|
||||
|
||||
const handleSharePointFilesSelected = async (sharePointFiles: any[]) => {
|
||||
try {
|
||||
await handleSharePointFiles(sharePointFiles);
|
||||
setIsSharePointDialogOpen(false);
|
||||
} catch (error) {
|
||||
console.error('SharePoint file processing error:', error);
|
||||
}
|
||||
};
|
||||
if (isUploadDisabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleButtonClick = () => {
|
||||
const handleLocalFileClick = () => {
|
||||
// necessary to reset the input
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
const dropdownItems = [
|
||||
{
|
||||
label: localize('com_files_upload_local_machine'),
|
||||
onClick: handleLocalFileClick,
|
||||
icon: <Folder className="icon-md" />,
|
||||
},
|
||||
{
|
||||
label: localize('com_files_upload_sharepoint'),
|
||||
onClick: () => setIsSharePointDialogOpen(true),
|
||||
icon: <SharePointIcon className="icon-md" />,
|
||||
},
|
||||
];
|
||||
const menuTrigger = (
|
||||
<Ariakit.MenuButton className="btn btn-neutral border-token-border-light relative h-9 w-full rounded-lg font-medium">
|
||||
<div className="flex w-full items-center justify-center gap-1">
|
||||
<AttachmentIcon className="text-token-text-primary h-4 w-4" />
|
||||
{localize('com_ui_upload_file_context')}
|
||||
</div>
|
||||
</Ariakit.MenuButton>
|
||||
);
|
||||
return (
|
||||
<div className="w-full">
|
||||
<HoverCard openDelay={50}>
|
||||
|
|
@ -101,26 +142,42 @@ export default function FileContext({
|
|||
Wrapper={({ children }) => <div className="flex flex-wrap gap-2">{children}</div>}
|
||||
/>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
disabled={!agent_id}
|
||||
className="btn btn-neutral border-token-border-light relative h-9 w-full rounded-lg font-medium"
|
||||
onClick={handleButtonClick}
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-1">
|
||||
<AttachmentIcon className="text-token-text-primary h-4 w-4" />
|
||||
<input
|
||||
multiple={true}
|
||||
type="file"
|
||||
style={{ display: 'none' }}
|
||||
tabIndex={-1}
|
||||
ref={fileInputRef}
|
||||
disabled={!agent_id}
|
||||
onChange={handleFileChange}
|
||||
{sharePointEnabled ? (
|
||||
<>
|
||||
<DropdownPopup
|
||||
gutter={2}
|
||||
menuId="file-search-upload-menu"
|
||||
isOpen={isPopoverActive}
|
||||
setIsOpen={setIsPopoverActive}
|
||||
trigger={menuTrigger}
|
||||
items={dropdownItems}
|
||||
modal={true}
|
||||
unmountOnHide={true}
|
||||
/>
|
||||
{localize('com_ui_upload_file_context')}
|
||||
</div>
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
disabled={!agent_id}
|
||||
className="btn btn-neutral border-token-border-light relative h-9 w-full rounded-lg font-medium"
|
||||
onClick={handleLocalFileClick}
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-1">
|
||||
<AttachmentIcon className="text-token-text-primary h-4 w-4" />
|
||||
|
||||
{localize('com_ui_upload_file_context')}
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
<input
|
||||
multiple={true}
|
||||
type="file"
|
||||
style={{ display: 'none' }}
|
||||
tabIndex={-1}
|
||||
ref={fileInputRef}
|
||||
disabled={!agent_id}
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
</div>
|
||||
{/* Disabled Message */}
|
||||
{agent_id ? null : (
|
||||
|
|
@ -129,6 +186,14 @@ export default function FileContext({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<SharePointPickerDialog
|
||||
isOpen={isSharePointDialogOpen}
|
||||
onOpenChange={setIsSharePointDialogOpen}
|
||||
onFilesSelected={handleSharePointFilesSelected}
|
||||
isDownloading={isProcessing}
|
||||
downloadProgress={downloadProgress}
|
||||
maxSelectionCount={endpointFileConfig?.fileLimit}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { useState, useRef } from 'react';
|
||||
import { Folder } from 'lucide-react';
|
||||
import * as Ariakit from '@ariakit/react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { AttachmentIcon } from '@librechat/client';
|
||||
import { SharePointIcon, AttachmentIcon, DropdownPopup } from '@librechat/client';
|
||||
import {
|
||||
EModelEndpoint,
|
||||
EToolResources,
|
||||
|
|
@ -9,10 +11,12 @@ import {
|
|||
fileConfig as defaultFileConfig,
|
||||
} from 'librechat-data-provider';
|
||||
import type { ExtendedFile, AgentForm } from '~/common';
|
||||
import useSharePointFileHandling from '~/hooks/Files/useSharePointFileHandling';
|
||||
import { useGetFileConfig, useGetStartupConfig } from '~/data-provider';
|
||||
import { useFileHandling, useLocalize, useLazyEffect } from '~/hooks';
|
||||
import { SharePointPickerDialog } from '~/components/SharePoint';
|
||||
import FileRow from '~/components/Chat/Input/Files/FileRow';
|
||||
import FileSearchCheckbox from './FileSearchCheckbox';
|
||||
import { useGetFileConfig } from '~/data-provider';
|
||||
import { useChatContext } from '~/Providers';
|
||||
|
||||
export default function FileSearch({
|
||||
|
|
@ -27,6 +31,11 @@ export default function FileSearch({
|
|||
const { watch } = useFormContext<AgentForm>();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [files, setFiles] = useState<Map<string, ExtendedFile>>(new Map());
|
||||
const [isPopoverActive, setIsPopoverActive] = useState(false);
|
||||
const [isSharePointDialogOpen, setIsSharePointDialogOpen] = useState(false);
|
||||
|
||||
// Get startup configuration for SharePoint feature flag
|
||||
const { data: startupConfig } = useGetStartupConfig();
|
||||
|
||||
const { data: fileConfig = defaultFileConfig } = useGetFileConfig({
|
||||
select: (data) => mergeFileConfig(data),
|
||||
|
|
@ -38,6 +47,12 @@ export default function FileSearch({
|
|||
fileSetter: setFiles,
|
||||
});
|
||||
|
||||
const { handleSharePointFiles, isProcessing, downloadProgress } = useSharePointFileHandling({
|
||||
overrideEndpoint: EModelEndpoint.agents,
|
||||
additionalMetadata: { agent_id, tool_resource: EToolResources.file_search },
|
||||
fileSetter: setFiles,
|
||||
});
|
||||
|
||||
useLazyEffect(
|
||||
() => {
|
||||
if (_files) {
|
||||
|
|
@ -53,6 +68,17 @@ export default function FileSearch({
|
|||
const endpointFileConfig = fileConfig.endpoints[EModelEndpoint.agents];
|
||||
const isUploadDisabled = endpointFileConfig.disabled ?? false;
|
||||
|
||||
const sharePointEnabled = startupConfig?.sharePointFilePickerEnabled;
|
||||
const disabledUploadButton = !agent_id || fileSearchChecked === false;
|
||||
|
||||
const handleSharePointFilesSelected = async (sharePointFiles: any[]) => {
|
||||
try {
|
||||
await handleSharePointFiles(sharePointFiles);
|
||||
setIsSharePointDialogOpen(false);
|
||||
} catch (error) {
|
||||
console.error('SharePoint file processing error:', error);
|
||||
}
|
||||
};
|
||||
if (isUploadDisabled) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -65,6 +91,38 @@ export default function FileSearch({
|
|||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
const handleLocalFileClick = () => {
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
const dropdownItems = [
|
||||
{
|
||||
label: localize('com_files_upload_local_machine'),
|
||||
onClick: handleLocalFileClick,
|
||||
icon: <Folder className="icon-md" />,
|
||||
},
|
||||
{
|
||||
label: localize('com_files_upload_sharepoint'),
|
||||
onClick: () => setIsSharePointDialogOpen(true),
|
||||
icon: <SharePointIcon className="icon-md" />,
|
||||
},
|
||||
];
|
||||
|
||||
const menuTrigger = (
|
||||
<Ariakit.MenuButton
|
||||
disabled={disabledUploadButton}
|
||||
className="btn btn-neutral border-token-border-light relative h-9 w-full rounded-lg font-medium"
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-1">
|
||||
<AttachmentIcon className="text-token-text-primary h-4 w-4" />
|
||||
{localize('com_ui_upload_file_search')}
|
||||
</div>
|
||||
</Ariakit.MenuButton>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="mb-1.5 flex items-center gap-2">
|
||||
|
|
@ -86,26 +144,39 @@ export default function FileSearch({
|
|||
Wrapper={({ children }) => <div className="flex flex-wrap gap-2">{children}</div>}
|
||||
/>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
disabled={!agent_id || fileSearchChecked === false}
|
||||
className="btn btn-neutral border-token-border-light relative h-9 w-full rounded-lg font-medium"
|
||||
onClick={handleButtonClick}
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-1">
|
||||
<AttachmentIcon className="text-token-text-primary h-4 w-4" />
|
||||
<input
|
||||
multiple={true}
|
||||
type="file"
|
||||
style={{ display: 'none' }}
|
||||
tabIndex={-1}
|
||||
ref={fileInputRef}
|
||||
disabled={!agent_id || fileSearchChecked === false}
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
{localize('com_ui_upload_file_search')}
|
||||
</div>
|
||||
</button>
|
||||
{sharePointEnabled ? (
|
||||
<DropdownPopup
|
||||
gutter={2}
|
||||
menuId="file-search-upload-menu"
|
||||
isOpen={isPopoverActive}
|
||||
setIsOpen={setIsPopoverActive}
|
||||
trigger={menuTrigger}
|
||||
items={dropdownItems}
|
||||
modal={true}
|
||||
unmountOnHide={true}
|
||||
/>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
disabled={disabledUploadButton}
|
||||
className="btn btn-neutral border-token-border-light relative h-9 w-full rounded-lg font-medium"
|
||||
onClick={handleButtonClick}
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-1">
|
||||
<AttachmentIcon className="text-token-text-primary h-4 w-4" />
|
||||
{localize('com_ui_upload_file_search')}
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
<input
|
||||
multiple={true}
|
||||
type="file"
|
||||
style={{ display: 'none' }}
|
||||
tabIndex={-1}
|
||||
ref={fileInputRef}
|
||||
disabled={disabledUploadButton}
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
</div>
|
||||
{/* Disabled Message */}
|
||||
{agent_id ? null : (
|
||||
|
|
@ -114,6 +185,16 @@ export default function FileSearch({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<SharePointPickerDialog
|
||||
isOpen={isSharePointDialogOpen}
|
||||
onOpenChange={setIsSharePointDialogOpen}
|
||||
onFilesSelected={handleSharePointFilesSelected}
|
||||
disabled={disabledUploadButton}
|
||||
isDownloading={isProcessing}
|
||||
downloadProgress={downloadProgress}
|
||||
maxSelectionCount={endpointFileConfig?.fileLimit}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
640
client/src/components/SidePanel/Agents/config.ts
Normal file
640
client/src/components/SidePanel/Agents/config.ts
Normal file
|
|
@ -0,0 +1,640 @@
|
|||
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
||||
export type ExtFilters =
|
||||
| 'folder'
|
||||
| 'site'
|
||||
| 'documentLibrary'
|
||||
| 'list'
|
||||
| 'onenote'
|
||||
| 'file'
|
||||
| 'media'
|
||||
| 'photo'
|
||||
| 'video'
|
||||
| 'audio'
|
||||
| 'document'
|
||||
| 'listItem'
|
||||
| 'playlist'
|
||||
| 'syntexTemplate'
|
||||
| 'syntexSnippet'
|
||||
| 'syntexField'
|
||||
| `.${string}`;
|
||||
|
||||
//NOTE: IItem type references the following docs: https://learn.microsoft.com/en-us/graph/api/resources/driveitem?view=graph-rest-1.0#properties
|
||||
export type IItem = Record<string, any>;
|
||||
export type SPPickerConfig = {
|
||||
sdk: '8.0';
|
||||
/**
|
||||
* Establishes the messaging parameters used to setup the post message communications between
|
||||
* picker and host application
|
||||
*/
|
||||
messaging: {
|
||||
/**
|
||||
* A unique id assigned by the host app to this File Picker instance.
|
||||
* This should ideally be a new GUID generated by the host.
|
||||
*/
|
||||
channelId: string;
|
||||
/**
|
||||
* The host app's authority, used as the target origin for post-messaging.
|
||||
*/
|
||||
origin: string;
|
||||
/**
|
||||
* Whether or not the host app window will need to identify itself.
|
||||
*/
|
||||
identifyParent?: boolean;
|
||||
/**
|
||||
* Whether or not the client app must wait for a 'configure' command to be sent by the host before rendering.
|
||||
*/
|
||||
waitForConfiguration?: boolean;
|
||||
/**
|
||||
* Override timeout for acknowledgement messages.
|
||||
*/
|
||||
acknowledgeTimeout?: number;
|
||||
/**
|
||||
* Override timeout for the initialization handshake.
|
||||
*/
|
||||
initializeTimeout?: number;
|
||||
/**
|
||||
* Override timeout for command responses.
|
||||
*/
|
||||
resultTimeout?: number;
|
||||
};
|
||||
/**
|
||||
* Configuration for the entry location to which the File Picker will navigate on load.
|
||||
* The File Picker app will prioritize path-based navigation if provided, falling back to other address forms
|
||||
* on error (in case of Site redirection or content rename) or if path information is not provided.
|
||||
*/
|
||||
entry: {
|
||||
sharePoint?: {
|
||||
/**
|
||||
* Specify an exact SharePoint content location by path segments.
|
||||
*/
|
||||
byPath?: {
|
||||
/**
|
||||
* Full URL to the root of a Web, or server-relative URL.
|
||||
* @example
|
||||
* 'https://contoso-my.sharepoint.com/personal/user_contoso_com'
|
||||
* @example
|
||||
* '/path/to/web'
|
||||
* @example
|
||||
* 'subweb'
|
||||
*/
|
||||
web?: string;
|
||||
/**
|
||||
* Full URL or path segement to identity a List.
|
||||
* If not preceded with a `/` or a URL scheme, this is assumed to be a list in the specified web.
|
||||
* @example
|
||||
* 'Shared Documents'
|
||||
* @example
|
||||
* '/path/to/web/Shared Documents'
|
||||
* @example
|
||||
* 'https://contoso.sharepoint.com/path/to/web/Shared Documents'
|
||||
*/
|
||||
list?: string;
|
||||
/**
|
||||
* Path segment to a folder within a list, or a server-relative URL to a folder.
|
||||
* @example
|
||||
* 'General'
|
||||
* @example
|
||||
* 'foo/bar'
|
||||
* @example
|
||||
* '/path/to/web/Shared Documents/General'
|
||||
*/
|
||||
folder?: string;
|
||||
/**
|
||||
* Auto fallback to root folder if the specified entry sub folder doesn't exist.
|
||||
*/
|
||||
fallbackToRoot?: boolean;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Indicates that File Picker should start in the Site Pivot
|
||||
* This pivot is only supported in OneDrive for Business
|
||||
*/
|
||||
site?: {};
|
||||
/**
|
||||
* Indicates that File Picker should start in the OAL (My Organization) Pivot
|
||||
* This pivot is only supported in OneDrive for Business
|
||||
*/
|
||||
myOrganization?: {};
|
||||
/**
|
||||
* Indicates that the File Picker should start in the user's OneDrive.
|
||||
*/
|
||||
oneDrive?: {
|
||||
/**
|
||||
* Specifies that File Picker should start in the user's Files tab.
|
||||
*/
|
||||
files?: {
|
||||
/**
|
||||
* Path segment for sub-folder within the user's OneDrive for Business.
|
||||
* @example
|
||||
* 'Pictures'
|
||||
* @example
|
||||
* '/personal/user_contoso_com/Documents/Attachments'
|
||||
*/
|
||||
folder?: string;
|
||||
/**
|
||||
* Auto fallback to root folder if the specified entry sub folder doesn't exist.
|
||||
*/
|
||||
fallbackToRoot?: boolean;
|
||||
};
|
||||
/**
|
||||
* Indicates that File Picker should start in the user's recent files.
|
||||
*/
|
||||
recent?: {};
|
||||
/**
|
||||
* Indicates that File Picker should start in the files shared with the user.
|
||||
*/
|
||||
sharedWithMe?: {};
|
||||
/**
|
||||
* Indicates that File Picker should start in the user's photos.
|
||||
* This pivot is only available in OneDrive for Consumer
|
||||
*/
|
||||
photos?: {};
|
||||
};
|
||||
sortBy?: {
|
||||
/**
|
||||
* Name of the field *in SharePoint* on which to sort.
|
||||
*/
|
||||
fieldName: string;
|
||||
/**
|
||||
* Whether or not to sort in ascending order. Default is `true`.
|
||||
*/
|
||||
isAscending?: boolean;
|
||||
};
|
||||
filterBy?: {
|
||||
/**
|
||||
* Name of the field *in SharePoint* on which to filter on.
|
||||
*/
|
||||
fieldName: string;
|
||||
/**
|
||||
* Filter value
|
||||
*/
|
||||
value: string;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Specifies how to enable a Search behavior.
|
||||
*/
|
||||
search?: {
|
||||
enabled: boolean;
|
||||
};
|
||||
/**
|
||||
* Configuration for handling authentication requests from the embedded app.
|
||||
* Presence of this object (even if empty) indicates that the host will handle authentication.
|
||||
* Omitting this will make the embedded content attempt to rely on cookies.
|
||||
*/
|
||||
authentication?: {
|
||||
/**
|
||||
* @default true
|
||||
*/
|
||||
enabled?: boolean;
|
||||
/**
|
||||
* Indicates support for individual token types.
|
||||
*/
|
||||
tokens?: {
|
||||
/**
|
||||
* @defaultValue true
|
||||
*/
|
||||
graph?: boolean;
|
||||
/**
|
||||
* @defaultValue true
|
||||
*/
|
||||
sharePoint?: boolean;
|
||||
/**
|
||||
* @defaultValue false
|
||||
*/
|
||||
substrate?: boolean;
|
||||
};
|
||||
/**
|
||||
* Indicates that the host app can handle 'claims' challenges.
|
||||
*/
|
||||
claimsChallenge?: {
|
||||
/**
|
||||
* @default false
|
||||
*/
|
||||
enabled?: boolean;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Configures what types of items are allowed to be picked within the experience.
|
||||
* Note that the default configuration accounts for the expected authentication capabilities of the host app.
|
||||
* Depending on what else is enabled by the host, the host may be expected to provide tokens for more services and scopes.
|
||||
*/
|
||||
typesAndSources?: {
|
||||
/**
|
||||
* Specifies the general category of items picked. Switches between 'file' vs. 'folder' picker mode,
|
||||
* or a general-purpose picker.
|
||||
* @default 'all'
|
||||
*/
|
||||
mode?: 'files' | 'folders' | 'all';
|
||||
/**
|
||||
* `filters` options: file extension, i.e. .xlsx, .docx, .ppt, etc.
|
||||
* `filters` options: 'photo', 'folder', 'video', 'documentLibrary'
|
||||
*/
|
||||
filters?: ExtFilters[];
|
||||
/**
|
||||
* Specifies a filter for *where* the item may come from.
|
||||
*/
|
||||
locations?: {
|
||||
/**
|
||||
* Items may only come from the user's OneDrive.
|
||||
*/
|
||||
oneDrive?: {};
|
||||
/**
|
||||
* Items may only come from a specific location within SharePoint.
|
||||
*/
|
||||
sharePoint?: {
|
||||
byPath?: {
|
||||
web?: string;
|
||||
list?: string;
|
||||
folder?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Specifies filtering based on user access level.
|
||||
*/
|
||||
access?: {
|
||||
/**
|
||||
* Filter for requires user access level for picked items. Default is `'read'`.
|
||||
*/
|
||||
mode?: 'read' | 'read-write';
|
||||
};
|
||||
/**
|
||||
* Specifies which pivots the user may access while browsing files and lists.
|
||||
* Note that if a pivot is disabled here but still targeted in `entry`, it will still be visible in the nav.
|
||||
*/
|
||||
pivots?: {
|
||||
/**
|
||||
* Show "My files".
|
||||
*/
|
||||
oneDrive?: boolean;
|
||||
/**
|
||||
* Show "Recent".
|
||||
*/
|
||||
recent?: boolean;
|
||||
/**
|
||||
* Show "Shared"
|
||||
*/
|
||||
shared?: boolean;
|
||||
/**
|
||||
* Show "Quick access".
|
||||
*/
|
||||
sharedLibraries?: boolean;
|
||||
/**
|
||||
* Show "My organization".
|
||||
* This pivot is only supported in OneDrive for Business
|
||||
*/
|
||||
myOrganization?: boolean;
|
||||
/**
|
||||
* Show the site pivot
|
||||
* This pivot is only supported in OneDrive for Business
|
||||
*/
|
||||
site?: boolean;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Configuration for what item types may be selected within the picker and returned to the host.
|
||||
*/
|
||||
selection?: {
|
||||
/**
|
||||
* Controls how selection works within the list.
|
||||
* @default 'single' for the Picker.
|
||||
*/
|
||||
mode?: 'single' | 'multiple' | 'pick';
|
||||
/**
|
||||
* Whether or not to allow the user to maintain a selection across folders and pivots.
|
||||
*/
|
||||
enablePersistence?: boolean;
|
||||
/**
|
||||
* Whether or not the host expects to be notified whenever selection changes.
|
||||
*/
|
||||
enableNotifications?: boolean;
|
||||
/**
|
||||
* The maximum number of items which may be selected.
|
||||
*/
|
||||
maximumCount?: number;
|
||||
/**
|
||||
* A set of items to pre-select.
|
||||
*/
|
||||
sourceItems?: IItem[];
|
||||
};
|
||||
/**
|
||||
* Configures how commands behave within the experience.
|
||||
*/
|
||||
commands?: {
|
||||
/**
|
||||
* Specifies the behavior for file-picking.
|
||||
*/
|
||||
pick?: {
|
||||
/**
|
||||
* A special action to perform when picking the file, before handing the result
|
||||
* back to the host app.
|
||||
*/
|
||||
action?: 'select' | 'share' | 'download' | 'move';
|
||||
/**
|
||||
* A custom label to apply to the button which picks files.
|
||||
* This must be localized by the host app if supplied.
|
||||
*/
|
||||
label?: string;
|
||||
/**
|
||||
* Configures the 'move' action for picking files.
|
||||
*/
|
||||
move?: {
|
||||
sourceItems?: IItem[];
|
||||
};
|
||||
/**
|
||||
* Configures the 'copy' action for picking files.
|
||||
*/
|
||||
copy?: {
|
||||
sourceItems?: IItem[];
|
||||
};
|
||||
/**
|
||||
* Configures the 'select' action for picking files.
|
||||
*/
|
||||
select?: {
|
||||
/**
|
||||
* Specify if we want download urls to be returned when items are selected.
|
||||
*/
|
||||
urls?: {
|
||||
download?: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Specifies the behavior for closing the experience.
|
||||
*/
|
||||
close?: {
|
||||
/**
|
||||
* A custom label to apply to the 'cancel' button.
|
||||
* This must be localized by the host app if supplied.
|
||||
*/
|
||||
label?: string;
|
||||
};
|
||||
/**
|
||||
* Behavior for a "Browse this device" command to pick local files.
|
||||
*/
|
||||
browseThisDevice?: {
|
||||
enabled?: boolean;
|
||||
label?: string;
|
||||
mode?: 'upload' | 'pick';
|
||||
};
|
||||
/**
|
||||
* Behavior for a "From a link" command to pick from a link.
|
||||
*/
|
||||
fromALink?: {
|
||||
enabled?: boolean;
|
||||
mode?: 'nav' | 'pivot';
|
||||
};
|
||||
/**
|
||||
* Behavior for a "Switch account" command.
|
||||
*/
|
||||
switchAccount?: {
|
||||
mode?: 'host' | 'none';
|
||||
};
|
||||
/**
|
||||
* Behavior for a "Manage accounts" command.
|
||||
*/
|
||||
manageAccounts?: {
|
||||
mode?: 'host' | 'none';
|
||||
label?: string;
|
||||
};
|
||||
/**
|
||||
* Behavior for "Upload"
|
||||
*/
|
||||
upload?: {
|
||||
enabled?: boolean;
|
||||
};
|
||||
/**
|
||||
* Behavior for "Create folder"
|
||||
*/
|
||||
createFolder?: {
|
||||
enabled?: boolean;
|
||||
};
|
||||
/**
|
||||
* Behavior for "Filter by" in the column headers.
|
||||
*/
|
||||
filterByColumn?: {
|
||||
mode?: 'panel' | 'menu';
|
||||
};
|
||||
/**
|
||||
* How to handle actions defined by custom formatters.
|
||||
*/
|
||||
customFormatter?: {
|
||||
actions?: {
|
||||
key: string;
|
||||
mode?: 'host' | 'none';
|
||||
}[];
|
||||
};
|
||||
/**
|
||||
* How to handle specified values for `key` in custom commands
|
||||
* in the tray, nav, or command bar.
|
||||
*/
|
||||
custom?: {
|
||||
actions?: {
|
||||
key: string;
|
||||
/**
|
||||
* Filters defining what types of items the action operates on.
|
||||
* If specified, the action will only be available for items which match the given filters.
|
||||
*/
|
||||
filters?: ExtFilters[];
|
||||
/**
|
||||
* How the action is invoked.
|
||||
* 'host': Invokes a `custom` command message against the host app.
|
||||
* 'none': Disables the action.
|
||||
*/
|
||||
mode?: 'host' | 'none';
|
||||
/**
|
||||
* Selection criteria to which the item applies.
|
||||
*/
|
||||
selection?: 'single' | 'multiple' | 'current' | 'none';
|
||||
}[];
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Specifies accessibility cues such as auto-focus behaviors.
|
||||
*/
|
||||
accessibility?: {
|
||||
/**
|
||||
* Whether or not to 'trap focus' within the component. If this is enabled, tab-stops will loop from the last element back to the left navigation automatically.
|
||||
* This is useful if the components's frame is hosted as the only content of a modal overlay and focus should not jump to the outside content.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
enableFocusTrap?: boolean;
|
||||
/**
|
||||
* Whether or not the component should immediately grab focus once the content has loaded.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
trapFocusOnLoad?: boolean;
|
||||
/**
|
||||
* Whether or not to force the currently-focused element within the component to be highlighted.
|
||||
* By default, the focused element is highlighted if the user navigates elements with the keyboard but not when the user interacts via the mouse.
|
||||
* However, if a host application launches the component due to keyboard input it should set this flag to `true` to ensure continuity of behavior.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
showFocusOnLoad?: boolean;
|
||||
};
|
||||
tray?: {
|
||||
/**
|
||||
* Configures the commands normally used to pick files or close the picker.
|
||||
*/
|
||||
commands?: {
|
||||
/**
|
||||
* A key to differentiate the command from others.
|
||||
*/
|
||||
key: string;
|
||||
/**
|
||||
* A custom string for the command.
|
||||
* Must be localized by the host.
|
||||
*/
|
||||
label?: string;
|
||||
/**
|
||||
* The action to perform when the button is clicked.
|
||||
*/
|
||||
action: 'pick' | 'close' | 'custom';
|
||||
/**
|
||||
* If `'pick'` is specified, which pick behavior to use.
|
||||
*/
|
||||
pick?: {
|
||||
action: 'select' | 'share' | 'download' | 'move';
|
||||
};
|
||||
/**
|
||||
* Whether the button should show as the primary button.
|
||||
*/
|
||||
primary?: boolean;
|
||||
/**
|
||||
* Whether the button should remain visible at all times even if unavailable.
|
||||
*/
|
||||
permanent?: boolean;
|
||||
}[];
|
||||
/**
|
||||
* Whether or not the picker tray might be provided by the host instead.
|
||||
* @defaultValue 'default'
|
||||
*/
|
||||
mode?: 'host' | 'default';
|
||||
/**
|
||||
* Configures a component to render in the picker tray to the left of the commands.
|
||||
* @default 'selection-summary'
|
||||
*/
|
||||
prompt?: 'keep-sharing' | 'selection-summary' | 'selection-editor' | 'save-as' | 'none';
|
||||
/**
|
||||
* Configures use of the 'save-as' prompt.
|
||||
*/
|
||||
saveAs?: {
|
||||
/**
|
||||
* Default file name to show in 'save-as' prompt.
|
||||
*/
|
||||
fileName?: string;
|
||||
};
|
||||
/**
|
||||
* Settings for handling conflicts with existing file names.
|
||||
*/
|
||||
conflicts?: {
|
||||
/**
|
||||
* How to handle when a file name matches an existing file.
|
||||
* `'warn'` - Show a prompt to ask the user to confirm the choice.
|
||||
* `'block'` - Block the choice as an error.
|
||||
* `'accept'` - Accept the choice automatically.
|
||||
* `'none'` - Do not try to match with existing items.
|
||||
*/
|
||||
mode?: 'warn' | 'block' | 'accept' | 'none';
|
||||
};
|
||||
/**
|
||||
* Configures use of the 'keep-sharing' prompt.
|
||||
*/
|
||||
keepSharing?: {
|
||||
active?: boolean;
|
||||
};
|
||||
};
|
||||
leftNav?: {
|
||||
/**
|
||||
* Whether or not a Left Nav should be rendered by the embedded content.
|
||||
*/
|
||||
enabled?: boolean;
|
||||
/**
|
||||
* Mode of presentation of the nav.
|
||||
* If the nav is enabled but this is set to `host`, the embedded app
|
||||
* will show a button to ask the host app to show a nav.
|
||||
*/
|
||||
mode?: 'host' | 'default';
|
||||
/**
|
||||
* Indicates whether the left nav will be initially modal.
|
||||
*/
|
||||
initialModality?: 'modal' | 'hidden';
|
||||
|
||||
/**
|
||||
* Type of left nav
|
||||
*/
|
||||
preset?: 'oneDrive' | 'current-site';
|
||||
|
||||
/**
|
||||
* Custom commands to insert at the end of the left nav. Will appear before the default set.
|
||||
*/
|
||||
commands?: {
|
||||
/**
|
||||
* Name to use when notifying the host that the command is being invoked.
|
||||
*/
|
||||
key: string;
|
||||
/**
|
||||
* Localized string to use for the button text.
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
* Type of action which will be performed when the command is clicked.
|
||||
* 'custom': Configured via `commands.custom`.
|
||||
*/
|
||||
action: 'custom' | 'pick' | 'close' | 'browse-this-device';
|
||||
/**
|
||||
* Name of a Fluent icon to use for the command button.
|
||||
*/
|
||||
icon?: string;
|
||||
}[];
|
||||
};
|
||||
/**
|
||||
* The theme to use for the file-picker. Will change the coloring.
|
||||
* Note: custom theme objects are expected in addition to the strings below
|
||||
* @default 'default': Light theme
|
||||
*/
|
||||
theme?: 'default' | 'dark' | 'lists';
|
||||
list?: {
|
||||
/**
|
||||
* A custom override for the initial list layout.
|
||||
*/
|
||||
layout?: {
|
||||
/**
|
||||
* Sets the preferred starting layout for the initial content.
|
||||
*/
|
||||
type?: 'details' | 'compact-details' | 'tiles';
|
||||
};
|
||||
/**
|
||||
* Configures scrolling behavior within the Picker.
|
||||
*/
|
||||
scrolling?: {
|
||||
enableStickyHeaders?: boolean;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Provides a header title for the Picker.
|
||||
*/
|
||||
title?: string;
|
||||
/**
|
||||
* Specifies customizations for specific pivots
|
||||
*/
|
||||
pivots?: {
|
||||
/**
|
||||
* Customize the site pivot
|
||||
*/
|
||||
site?: {
|
||||
byPath?: {
|
||||
/**
|
||||
* Chose the site url to use for this pivot
|
||||
* Required to show the site pivot, if undefined
|
||||
* the site pivot will not be shown
|
||||
*/
|
||||
web?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue