diff --git a/src/controls/imagePicker/selectFromSharePoint.tsx b/src/controls/imagePicker/selectFromSharePoint.tsx index 8c715506e..52a87ca7e 100644 --- a/src/controls/imagePicker/selectFromSharePoint.tsx +++ b/src/controls/imagePicker/selectFromSharePoint.tsx @@ -14,9 +14,14 @@ import { DialogBody, DialogContent, DialogSurface, + FluentProvider, Image, mergeClasses, Spinner, + teamsDarkTheme, + teamsHighContrastTheme, + teamsLightTheme, + Theme, tokens, } from '@fluentui/react-components'; @@ -31,8 +36,12 @@ import { RenderHeader } from './renderHeader/RenderHeader'; import { SelectStockImage } from './SelectStokImage'; import { UploadImageFiles } from './Upload'; import { useImagePickerStyles } from './useImagePickerStyles'; +import { has } from 'lodash'; +import { WebPartContext } from '@microsoft/sp-webpart-base'; +import { useTheme } from '@fluentui/react-theme-provider'; +import { createV9Theme } from '@fluentui/react-migration-v8-v9'; -const defaultImage = require("./constants/defaultImage.png"); +const defaultImage = require('./constants/defaultImage.png'); export interface ISelectFromSharePointProps { onFileSelected: (file: IFilePickerResult) => void; @@ -46,60 +55,110 @@ const SOURCE_ONEDRIVE = `AND path:https://*my.sharepoint.com`; const SOURCE_STOCK = `stockImages`; const SOURCE_UPLOAD = `upload`; -const acceptableExtensions: string[] = IMG_SUPPORTED_EXTENSIONS.split(","); -const queryExtensions = acceptableExtensions.map((ext) => `fileType:${ext}`).join(" OR "); +const acceptableExtensions: string[] = IMG_SUPPORTED_EXTENSIONS.split(','); +const queryExtensions = acceptableExtensions + .map((ext) => `fileType:${ext}`) + .join(' OR '); const PAGE_ITEMS = 50; -export const SelectFromSharePoint: React.FunctionComponent = ( - props: React.PropsWithChildren -) => { +export const SelectFromSharePoint: React.FunctionComponent< + ISelectFromSharePointProps +> = (props: React.PropsWithChildren) => { const { isOpen, onDismiss, onFileSelected } = props; const [isAdding, setIsAdding] = React.useState(false); - const [searchResults, setSearchResults] = React.useState([]); + const [searchResults, setSearchResults] = React.useState< + ISearchImagesResult[] + >([]); const appContext = useAtomValue(contextState); const { context } = appContext; const { searchImages } = useGraphAPI(context); const { downLoadSpOrOneDriveContent } = useSpAPI(context); - const [selectedImage, setSelectedImage] = React.useState(null); + const [selectedImage, setSelectedImage] = + React.useState(null); const refSelectedImage = React.useRef(null); const isScrolling = React.useRef(false); const styles = useImagePickerStyles(); const [isLoading, setIsLoading] = React.useState(false); const [source, setSource] = React.useState(SOURCE_SHAREPOINT); - const { getFileNameFromUrl, getFileNameWithoutExtension, getScrollPosition } = useUtils(); + const { getFileNameFromUrl, getFileNameWithoutExtension, getScrollPosition } = + useUtils(); const refStart = React.useRef(0); const refHasMore = React.useRef(false); + const [theme, setTheme] = React.useState(); + const currentSPTheme = useTheme(); + const [isInitialized, setIsInitialized] = React.useState(false); + const hasSelectedImage = React.useMemo( + () => selectedImage !== null, + [selectedImage] + ); - const hasSelectedImage = React.useMemo(() => selectedImage !== null, [selectedImage]); + React.useEffect(() => { + (async () => { + if (has(context, 'sdks.microsoftTeams.teamsJs.app.getContext')) { + const teamsContext = await ( + context as WebPartContext + ).sdks.microsoftTeams?.teamsJs.app.getContext(); + const teamsTheme = teamsContext.app.theme || 'default'; + switch (teamsTheme) { + case 'dark': + setTheme(teamsDarkTheme); + break; + case 'contrast': + setTheme(teamsHighContrastTheme); + break; + case 'default': + setTheme(teamsLightTheme); + break; + default: + setTheme(teamsLightTheme); + break; + } + } else { + setTheme(createV9Theme(currentSPTheme)); + } + setIsInitialized(true); + })().catch((error) => { + console.error('Error initializing theme:', error); + setTheme(createV9Theme(currentSPTheme)); + setIsInitialized(true); + }); + }, [context, currentSPTheme]); const getMoreResultsSearch = React.useCallback(async () => { if (source === SOURCE_STOCK || source === SOURCE_UPLOAD) return; if (!refHasMore.current) return; refStart.current += PAGE_ITEMS; try { - const results = await searchImages(`(${queryExtensions}) ${source}`, refStart.current); + const results = await searchImages( + `(${queryExtensions}) ${source}`, + refStart.current + ); const { fields, hasMoreResults } = results; refHasMore.current = hasMoreResults; setSearchResults((prev) => [...prev, ...fields]); } catch (error) { - console.error("[getMoreResultsSearch] error:", error); + console.error('[getMoreResultsSearch] error:', error); } }, [searchImages, source]); React.useEffect(() => { (async () => { - if (!context || source === SOURCE_STOCK || source === SOURCE_UPLOAD) return; + if (!context || source === SOURCE_STOCK || source === SOURCE_UPLOAD) + return; setIsLoading(true); setSelectedImage(null); refStart.current = 0; try { - const results = await searchImages(`(${queryExtensions}) ${source}`, refStart.current); + const results = await searchImages( + `(${queryExtensions}) ${source}`, + refStart.current + ); const { fields, hasMoreResults } = results; refHasMore.current = hasMoreResults; setSearchResults(fields); } catch (error) { - console.error("[useEffect] error:", error); + console.error('[useEffect] error:', error); } finally { setIsLoading(false); } @@ -108,14 +167,16 @@ export const SelectFromSharePoint: React.FunctionComponent { if (!refSelectedImage.current) return; - const { defaultEncodingURL, driveId, id, filename, fileType } = refSelectedImage.current; + const { defaultEncodingURL, driveId, id, filename, fileType } = + refSelectedImage.current; const image = driveId ? `${window.location.origin}/_api/v2.1/sites/${TENANT_NAME}/drives/${driveId}/items/${id}/thumbnails/0/c400x999/content?prefer=noredirect,closestavailablesize,extendCacheMaxAge` - : fileType === "svg" + : fileType === 'svg' ? defaultEncodingURL : defaultImage; const fileResult: IFilePickerResult = { - downloadFileContent: () => downLoadSpOrOneDriveContent(driveId, id, filename), + downloadFileContent: () => + downLoadSpOrOneDriveContent(driveId, id, filename), fileAbsoluteUrl: defaultEncodingURL, fileName: getFileNameFromUrl(defaultEncodingURL), fileNameWithoutExtension: getFileNameWithoutExtension(defaultEncodingURL), @@ -123,12 +184,22 @@ export const SelectFromSharePoint: React.FunctionComponent ( <> - - - - - - {isLoading ? : renderSelectedImage()} - - {source !== SOURCE_STOCK && ( - - {renderDialogActions} - - )} - - + + + + + +
+ + + + +
+ {isLoading ? ( + + ) : ( + renderSelectedImage() + )} +
+ {source !== SOURCE_STOCK && ( + + {renderDialogActions} + + )} +
+
+
); }; diff --git a/src/controls/imagePicker/uploadFiles/useUploadFilesStyles.ts b/src/controls/imagePicker/uploadFiles/useUploadFilesStyles.ts index eaed5c876..6abea7e6e 100644 --- a/src/controls/imagePicker/uploadFiles/useUploadFilesStyles.ts +++ b/src/controls/imagePicker/uploadFiles/useUploadFilesStyles.ts @@ -1,101 +1,96 @@ -import { - makeStyles, - shorthands, - tokens, -} from "@fluentui/react-components"; +import { makeStyles, shorthands, tokens } from '@fluentui/react-components'; -export const useUploadFilesStyles = - makeStyles({ - baseStyle: { - ...shorthands.flex("1"), - display: "flex", - flexDirection: "column", - alignItems: "center", - ...shorthands.padding("20px"), - ...shorthands.borderWidth("2px"), - ...shorthands.borderRadius("2px"), - ...shorthands.borderColor(tokens.colorNeutralBackground5), - ...shorthands.borderStyle("dashed"), - /* backgroundColor: tokens.colorNeutralBackground3, */ +export const useUploadFilesStyles = makeStyles({ + baseStyle: { + ...shorthands.flex('auto'), + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + ...shorthands.padding('20px'), + ...shorthands.borderWidth('2px'), + ...shorthands.borderRadius('2px'), + ...shorthands.borderColor(tokens.colorNeutralBackground5), + ...shorthands.borderStyle('dashed'), + /* backgroundColor: tokens.colorNeutralBackground3, */ - ...shorthands.outline("none"), + ...shorthands.outline('none'), - "&:hover": { - ...shorthands.borderColor(tokens.colorNeutralBackground3Pressed), - cursor: "pointer", - }, + '&:hover': { + ...shorthands.borderColor(tokens.colorNeutralBackground3Pressed), + cursor: 'pointer', }, + }, - dragContainer: { - display: "flex", - flexDirection: "column", - alignItems: "center", - justifyContent: "center", - ...shorthands.gap("10px"), - }, + dragContainer: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + ...shorthands.gap('10px'), + }, - imagesContainer: { - display: "grid", - maxHeight: "500px", - gridTemplateColumns: "repeat(auto-fill, minmax(min(100%,150px), 1fr))", - ...shorthands.gap("10px"), - paddingTop: "15px", - paddingBottom: "15px", - overflowY: "auto", - scrollbarWidth: "thin", - }, + imagesContainer: { + display: 'grid', + maxHeight: '500px', + gridTemplateColumns: 'repeat(auto-fill, minmax(min(100%,150px), 1fr))', + ...shorthands.gap('10px'), + paddingTop: '15px', + paddingBottom: '15px', + overflowY: 'auto', + scrollbarWidth: 'thin', + }, - itemBody: { - display: "flex", - flexDirection: "column", - /* paddingBottom: "10px", */ - }, + itemBody: { + display: 'flex', + flexDirection: 'column', + /* paddingBottom: "10px", */ + }, - headerTitle: { - display: "-webkit-box", - "-webkit-line-clamp": "1", - "-webkit-box-orient": "vertical", - ...shorthands.overflow("hidden"), - breakWord: "break-all", - lineBreak: "anywhere", - textAlign: "start", - textOverflow: "ellipsis", - }, + headerTitle: { + display: '-webkit-box', + '-webkit-line-clamp': '1', + '-webkit-box-orient': 'vertical', + ...shorthands.overflow('hidden'), + breakWord: 'break-all', + lineBreak: 'anywhere', + textAlign: 'start', + textOverflow: 'ellipsis', + }, - bottomContainer: { - justifyContent: "end", - alignItems: "center", - display: "flex", - flexDirection: "row", - position: "absolute", - bottom: "15px", - right: "5px", - height: "20px" - }, - - card: { - height: "210px", - ...shorthands.borderRadius("6px"), - }, + bottomContainer: { + justifyContent: 'end', + alignItems: 'center', + display: 'flex', + flexDirection: 'row', + position: 'absolute', + bottom: '15px', + right: '5px', + height: '20px', + }, - deleteStyle: { - width: "16px", - height: "16px", - }, - iconRefreshStyle:{ - width: "16px", - height: "16px", - - }, - selectedImage: { - ...shorthands.border("3px", "solid", tokens.colorBrandBackground), - ...shorthands.padding("3px"), - }, - dragDropIconStyles : { - width: "28px", height: "28px", color: tokens.colorBrandBackground - }, - containerGlobalMarginTop: { - marginTop: "10px", - }, + card: { + height: '210px', + ...shorthands.borderRadius('6px'), + }, - }); + deleteStyle: { + width: '16px', + height: '16px', + }, + iconRefreshStyle: { + width: '16px', + height: '16px', + }, + selectedImage: { + ...shorthands.border('3px', 'solid', tokens.colorBrandBackground), + ...shorthands.padding('3px'), + }, + dragDropIconStyles: { + width: '28px', + height: '28px', + color: tokens.colorBrandBackground, + }, + containerGlobalMarginTop: { + marginTop: '10px', + }, +});