@lnco-ai/ui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1323 -0
- package/LICENSE +661 -0
- package/README.md +60 -0
- package/dist/Authorization/PreventGuestWrapper.d.ts +19 -0
- package/dist/Authorization/PreventGuestWrapper.js +15 -0
- package/dist/Authorization/RedirectionContent.d.ts +9 -0
- package/dist/Authorization/RedirectionContent.js +24 -0
- package/dist/Authorization/SignedInWrapper.d.ts +10 -0
- package/dist/Authorization/SignedInWrapper.js +21 -0
- package/dist/Avatar/Avatar.d.ts +24 -0
- package/dist/Avatar/Avatar.js +22 -0
- package/dist/Avatar/stringToColor.d.ts +12 -0
- package/dist/Avatar/stringToColor.js +31 -0
- package/dist/Avatar/stringToColor.test.d.ts +1 -0
- package/dist/Avatar/stringToColor.test.js +8 -0
- package/dist/Card/Card.d.ts +39 -0
- package/dist/Card/Card.js +76 -0
- package/dist/Card/CardHeader.d.ts +12 -0
- package/dist/Card/CardHeader.js +8 -0
- package/dist/Card/CardThumbnail.d.ts +10 -0
- package/dist/Card/CardThumbnail.js +14 -0
- package/dist/Card/FolderCard.d.ts +14 -0
- package/dist/Card/FolderCard.js +44 -0
- package/dist/Card/LinkCard.d.ts +11 -0
- package/dist/Card/LinkCard.js +41 -0
- package/dist/Card/components/CardThumbnail.d.ts +8 -0
- package/dist/Card/components/CardThumbnail.js +15 -0
- package/dist/Card/constants.d.ts +1 -0
- package/dist/Card/constants.js +1 -0
- package/dist/Collapse/Collapse.d.ts +10 -0
- package/dist/Collapse/Collapse.js +48 -0
- package/dist/Collapse/withCollapse.d.ts +7 -0
- package/dist/Collapse/withCollapse.js +8 -0
- package/dist/CookiesBanner/CookiesBanner.d.ts +10 -0
- package/dist/CookiesBanner/CookiesBanner.js +60 -0
- package/dist/CreativeCommons/CreativeCommons.d.ts +14 -0
- package/dist/CreativeCommons/CreativeCommons.js +75 -0
- package/dist/CreativeCommons/icons/By.d.ts +3 -0
- package/dist/CreativeCommons/icons/By.js +3 -0
- package/dist/CreativeCommons/icons/CCIconsProps.d.ts +3 -0
- package/dist/CreativeCommons/icons/CCIconsProps.js +1 -0
- package/dist/CreativeCommons/icons/Cc.d.ts +3 -0
- package/dist/CreativeCommons/icons/Cc.js +3 -0
- package/dist/CreativeCommons/icons/Cc0.d.ts +3 -0
- package/dist/CreativeCommons/icons/Cc0.js +3 -0
- package/dist/CreativeCommons/icons/Nc.d.ts +3 -0
- package/dist/CreativeCommons/icons/Nc.js +3 -0
- package/dist/CreativeCommons/icons/Nd.d.ts +3 -0
- package/dist/CreativeCommons/icons/Nd.js +3 -0
- package/dist/CreativeCommons/icons/Sa.d.ts +3 -0
- package/dist/CreativeCommons/icons/Sa.js +3 -0
- package/dist/CustomInitialLoader/CustomInitialLoader.d.ts +5 -0
- package/dist/CustomInitialLoader/CustomInitialLoader.js +25 -0
- package/dist/DrawerHeader/DrawerHeader.d.ts +7 -0
- package/dist/DrawerHeader/DrawerHeader.js +16 -0
- package/dist/GraaspLogo/EpflLogo.d.ts +5 -0
- package/dist/GraaspLogo/EpflLogo.js +3 -0
- package/dist/GraaspLogo/GraaspLogo.d.ts +8 -0
- package/dist/GraaspLogo/GraaspLogo.js +5 -0
- package/dist/Header/Header.d.ts +21 -0
- package/dist/Header/Header.js +33 -0
- package/dist/HeaderUserInformation/HeaderUserInformation.d.ts +22 -0
- package/dist/HeaderUserInformation/HeaderUserInformation.js +23 -0
- package/dist/ItemBadges/ItemBadges.d.ts +17 -0
- package/dist/ItemBadges/ItemBadges.js +10 -0
- package/dist/ItemFlag/ItemFlagButton.d.ts +9 -0
- package/dist/ItemFlag/ItemFlagButton.js +11 -0
- package/dist/ItemFlag/ItemFlagDialog.d.ts +14 -0
- package/dist/ItemFlag/ItemFlagDialog.js +25 -0
- package/dist/Loader/Loader.d.ts +7 -0
- package/dist/Loader/Loader.js +4 -0
- package/dist/Main/LogoHeader.d.ts +2 -0
- package/dist/Main/LogoHeader.js +5 -0
- package/dist/Main/Main.d.ts +59 -0
- package/dist/Main/Main.js +82 -0
- package/dist/MainMenu/MainMenu.d.ts +10 -0
- package/dist/MainMenu/MainMenu.js +6 -0
- package/dist/MainMenu/MenuItem/MenuItem.d.ts +16 -0
- package/dist/MainMenu/MenuItem/MenuItem.js +16 -0
- package/dist/MainMenu/hooks.d.ts +10 -0
- package/dist/MainMenu/hooks.js +21 -0
- package/dist/Navigation/CurrentItemNavigation.d.ts +15 -0
- package/dist/Navigation/CurrentItemNavigation.js +12 -0
- package/dist/Navigation/ExtraItemsMenu.d.ts +10 -0
- package/dist/Navigation/ExtraItemsMenu.js +24 -0
- package/dist/Navigation/ExtraItemsNavigation.d.ts +11 -0
- package/dist/Navigation/ExtraItemsNavigation.js +11 -0
- package/dist/Navigation/HomeMenu.d.ts +17 -0
- package/dist/Navigation/HomeMenu.js +28 -0
- package/dist/Navigation/ItemMenu.d.ts +16 -0
- package/dist/Navigation/ItemMenu.js +32 -0
- package/dist/Navigation/Navigation.d.ts +26 -0
- package/dist/Navigation/Navigation.js +15 -0
- package/dist/Navigation/ParentsNavigation.d.ts +10 -0
- package/dist/Navigation/ParentsNavigation.js +8 -0
- package/dist/Navigation/common/CenterAlignWrapper.d.ts +3 -0
- package/dist/Navigation/common/CenterAlignWrapper.js +7 -0
- package/dist/Navigation/common/NavigationLink.d.ts +8 -0
- package/dist/Navigation/common/NavigationLink.js +7 -0
- package/dist/Navigation/common/constants.d.ts +1 -0
- package/dist/Navigation/common/constants.js +1 -0
- package/dist/PlatformSwitch/PlatformSwitch.d.ts +57 -0
- package/dist/PlatformSwitch/PlatformSwitch.js +66 -0
- package/dist/PlatformSwitch/hooks.d.ts +30 -0
- package/dist/PlatformSwitch/hooks.js +49 -0
- package/dist/SearchInput/SearchInput.d.ts +20 -0
- package/dist/SearchInput/SearchInput.js +14 -0
- package/dist/Select/Select.d.ts +24 -0
- package/dist/Select/Select.js +7 -0
- package/dist/Sidebar/Sidebar.d.ts +11 -0
- package/dist/Sidebar/Sidebar.js +25 -0
- package/dist/StyledComponents/StyledBaseComponents.d.ts +11 -0
- package/dist/StyledComponents/StyledBaseComponents.js +11 -0
- package/dist/TextDisplay/TextDisplay.d.ts +5 -0
- package/dist/TextDisplay/TextDisplay.js +24 -0
- package/dist/TextDisplay/fixtures.d.ts +3 -0
- package/dist/TextDisplay/fixtures.js +84 -0
- package/dist/TextDisplay/withFlavor.d.ts +8 -0
- package/dist/TextDisplay/withFlavor.js +25 -0
- package/dist/TextEditor/TextEditor.d.ts +16 -0
- package/dist/TextEditor/TextEditor.js +74 -0
- package/dist/ThemeContext/LanguageSelect.d.ts +15 -0
- package/dist/ThemeContext/LanguageSelect.js +19 -0
- package/dist/ThemeContext/ThemeContext.d.ts +22 -0
- package/dist/ThemeContext/ThemeContext.js +31 -0
- package/dist/Thumbnail/Thumbnail.d.ts +28 -0
- package/dist/Thumbnail/Thumbnail.js +27 -0
- package/dist/Tree/Breadcrumbs.d.ts +14 -0
- package/dist/Tree/Breadcrumbs.js +30 -0
- package/dist/Tree/RowMenu.d.ts +12 -0
- package/dist/Tree/RowMenu.js +37 -0
- package/dist/Tree/RowMenus.d.ts +18 -0
- package/dist/Tree/RowMenus.js +7 -0
- package/dist/Tree/types.d.ts +6 -0
- package/dist/Tree/types.js +1 -0
- package/dist/UserSwitch/UserSwitch.d.ts +17 -0
- package/dist/UserSwitch/UserSwitch.js +46 -0
- package/dist/UserSwitch/UserSwitchWrapper.d.ts +37 -0
- package/dist/UserSwitch/UserSwitchWrapper.js +74 -0
- package/dist/appComponents/ErrorFallback.d.ts +57 -0
- package/dist/appComponents/ErrorFallback.js +72 -0
- package/dist/appComponents/QuestionLabel.d.ts +9 -0
- package/dist/appComponents/QuestionLabel.js +4 -0
- package/dist/appComponents/index.d.ts +6 -0
- package/dist/appComponents/index.js +5 -0
- package/dist/appComponents/statusChips/RequiredChip.d.ts +3 -0
- package/dist/appComponents/statusChips/RequiredChip.js +5 -0
- package/dist/appComponents/statusChips/SavedChip.d.ts +3 -0
- package/dist/appComponents/statusChips/SavedChip.js +5 -0
- package/dist/appComponents/statusChips/SubmittedChip.d.ts +3 -0
- package/dist/appComponents/statusChips/SubmittedChip.js +5 -0
- package/dist/appComponents/statusChips/types.d.ts +5 -0
- package/dist/appComponents/statusChips/types.js +1 -0
- package/dist/appComponents/types.d.ts +8 -0
- package/dist/appComponents/types.js +1 -0
- package/dist/apps.d.ts +1 -0
- package/dist/apps.js +1 -0
- package/dist/buttons/BookmarkButton/BookmarkButton.d.ts +24 -0
- package/dist/buttons/BookmarkButton/BookmarkButton.js +24 -0
- package/dist/buttons/Button/Button.d.ts +37 -0
- package/dist/buttons/Button/Button.js +4 -0
- package/dist/buttons/ChatboxButton/ChatboxButton.d.ts +15 -0
- package/dist/buttons/ChatboxButton/ChatboxButton.js +21 -0
- package/dist/buttons/CopyButton/CopyButton.d.ts +13 -0
- package/dist/buttons/CopyButton/CopyButton.js +14 -0
- package/dist/buttons/DeleteButton/DeleteButton.d.ts +12 -0
- package/dist/buttons/DeleteButton/DeleteButton.js +17 -0
- package/dist/buttons/DownloadButton/DownloadButton.d.ts +30 -0
- package/dist/buttons/DownloadButton/DownloadButton.js +20 -0
- package/dist/buttons/EditButton/EditButton.d.ts +13 -0
- package/dist/buttons/EditButton/EditButton.js +17 -0
- package/dist/buttons/LikeButton/LikeButton.d.ts +28 -0
- package/dist/buttons/LikeButton/LikeButton.js +10 -0
- package/dist/buttons/MenuItemButton.d.ts +11 -0
- package/dist/buttons/MenuItemButton.js +12 -0
- package/dist/buttons/MoveButton/MoveButton.d.ts +13 -0
- package/dist/buttons/MoveButton/MoveButton.js +19 -0
- package/dist/buttons/PinButton/PinButton.d.ts +15 -0
- package/dist/buttons/PinButton/PinButton.js +21 -0
- package/dist/buttons/SaveButton/SaveButton.d.ts +24 -0
- package/dist/buttons/SaveButton/SaveButton.js +4 -0
- package/dist/buttons/ShareButton/ShareButton.d.ts +14 -0
- package/dist/buttons/ShareButton/ShareButton.js +17 -0
- package/dist/buttons/hooks.d.ts +5 -0
- package/dist/buttons/hooks.js +14 -0
- package/dist/buttons/index.d.ts +12 -0
- package/dist/buttons/index.js +12 -0
- package/dist/constants.d.ts +11 -0
- package/dist/constants.js +12 -0
- package/dist/draggable/DraggableElement.d.ts +11 -0
- package/dist/draggable/DraggableElement.js +42 -0
- package/dist/draggable/DraggingWrapper.d.ts +23 -0
- package/dist/draggable/DraggingWrapper.js +20 -0
- package/dist/draggable/InBetween.d.ts +10 -0
- package/dist/draggable/InBetween.js +34 -0
- package/dist/draggable/types.d.ts +10 -0
- package/dist/draggable/types.js +1 -0
- package/dist/enums/errors.d.ts +3 -0
- package/dist/enums/errors.js +3 -0
- package/dist/hooks/useFullscreen.d.ts +5 -0
- package/dist/hooks/useFullscreen.js +37 -0
- package/dist/hooks/useMobileView.d.ts +4 -0
- package/dist/hooks/useMobileView.js +6 -0
- package/dist/icons/AnalyticsIcon.d.ts +15 -0
- package/dist/icons/AnalyticsIcon.js +7 -0
- package/dist/icons/BuildIcon.d.ts +15 -0
- package/dist/icons/BuildIcon.js +7 -0
- package/dist/icons/EtherpadIcon.d.ts +7 -0
- package/dist/icons/EtherpadIcon.js +7 -0
- package/dist/icons/H5PIcon.d.ts +6 -0
- package/dist/icons/H5PIcon.js +6 -0
- package/dist/icons/ItemIcon.d.ts +23 -0
- package/dist/icons/ItemIcon.js +75 -0
- package/dist/icons/LibraryIcon.d.ts +16 -0
- package/dist/icons/LibraryIcon.js +7 -0
- package/dist/icons/PlayIcon.d.ts +15 -0
- package/dist/icons/PlayIcon.js +7 -0
- package/dist/icons/ResizingIcon.d.ts +2 -0
- package/dist/icons/ResizingIcon.js +21 -0
- package/dist/icons/StyledSVGComponents.d.ts +35 -0
- package/dist/icons/StyledSVGComponents.js +53 -0
- package/dist/icons/index.d.ts +6 -0
- package/dist/icons/index.js +6 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.js +60 -0
- package/dist/itemLogin/ForbiddenContent.d.ts +20 -0
- package/dist/itemLogin/ForbiddenContent.js +4 -0
- package/dist/itemLogin/ForbiddenText.d.ts +8 -0
- package/dist/itemLogin/ForbiddenText.js +6 -0
- package/dist/itemLogin/ItemLoginScreen.d.ts +26 -0
- package/dist/itemLogin/ItemLoginScreen.js +72 -0
- package/dist/itemLogin/ItemLoginWrapper.d.ts +23 -0
- package/dist/itemLogin/ItemLoginWrapper.js +43 -0
- package/dist/itemLogin/constants.d.ts +1 -0
- package/dist/itemLogin/constants.js +1 -0
- package/dist/items/AppItem.d.ts +60 -0
- package/dist/items/AppItem.js +69 -0
- package/dist/items/DocumentItem.d.ts +19 -0
- package/dist/items/DocumentItem.js +25 -0
- package/dist/items/DownloadButtonFileItem.d.ts +9 -0
- package/dist/items/DownloadButtonFileItem.js +10 -0
- package/dist/items/EtherpadItem.d.ts +26 -0
- package/dist/items/EtherpadItem.js +15 -0
- package/dist/items/FileAudio.d.ts +9 -0
- package/dist/items/FileAudio.js +10 -0
- package/dist/items/FileImage.d.ts +7 -0
- package/dist/items/FileImage.js +10 -0
- package/dist/items/FileItem.d.ts +25 -0
- package/dist/items/FileItem.js +81 -0
- package/dist/items/FilePdf.d.ts +14 -0
- package/dist/items/FilePdf.js +29 -0
- package/dist/items/FileVideo.d.ts +8 -0
- package/dist/items/FileVideo.js +9 -0
- package/dist/items/H5PItem.d.ts +20 -0
- package/dist/items/H5PItem.js +80 -0
- package/dist/items/ItemSkeleton/ItemSkeleton.d.ts +12 -0
- package/dist/items/ItemSkeleton/ItemSkeleton.js +35 -0
- package/dist/items/LinkItem.d.ts +43 -0
- package/dist/items/LinkItem.js +97 -0
- package/dist/items/SizingWrapper.d.ts +6 -0
- package/dist/items/SizingWrapper.js +23 -0
- package/dist/items/appItemHooks.d.ts +27 -0
- package/dist/items/appItemHooks.js +124 -0
- package/dist/items/constants.d.ts +1 -0
- package/dist/items/constants.js +1 -0
- package/dist/items/iframeStyles.d.ts +4 -0
- package/dist/items/iframeStyles.js +5 -0
- package/dist/items/index.d.ts +5 -0
- package/dist/items/index.js +5 -0
- package/dist/items/withCaption.d.ts +19 -0
- package/dist/items/withCaption.js +58 -0
- package/dist/items/withResizing.d.ts +9 -0
- package/dist/items/withResizing.js +42 -0
- package/dist/text-editor.d.ts +2 -0
- package/dist/text-editor.js +2 -0
- package/dist/theme.d.ts +108 -0
- package/dist/theme.js +214 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.js +32 -0
- package/dist/upload/FileDropper/FileDropper.d.ts +50 -0
- package/dist/upload/FileDropper/FileDropper.js +43 -0
- package/dist/upload/UploadFileButton/UploadFileButton.d.ts +35 -0
- package/dist/upload/UploadFileButton/UploadFileButton.js +17 -0
- package/dist/utils/fixtures.d.ts +3 -0
- package/dist/utils/fixtures.js +7 -0
- package/dist/utils/storybook.d.ts +5 -0
- package/dist/utils/storybook.js +5 -0
- package/package.json +172 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Alert, Box, Skeleton } from '@mui/material';
|
|
3
|
+
import { memo, useEffect, useState } from 'react';
|
|
4
|
+
import { ItemType, MimeTypes, formatFileSize, getFileExtra, getS3FileExtra, } from '@lnco-ai/sdk';
|
|
5
|
+
import { Errors } from '../enums/errors.js';
|
|
6
|
+
import withCollapse from '../Collapse/withCollapse.js';
|
|
7
|
+
import { SCREEN_MAX_HEIGHT, UNEXPECTED_ERROR_MESSAGE } from '../constants.js';
|
|
8
|
+
import DownloadButtonFileItem from './DownloadButtonFileItem.js';
|
|
9
|
+
import FileAudio from './FileAudio.js';
|
|
10
|
+
import FileImage from './FileImage.js';
|
|
11
|
+
import FilePdf from './FilePdf.js';
|
|
12
|
+
import FileVideo from './FileVideo.js';
|
|
13
|
+
import { SizingWrapper } from './SizingWrapper.js';
|
|
14
|
+
import { CaptionWrapper } from './withCaption.js';
|
|
15
|
+
const FileItem = ({ content, fileUrl, defaultItem, errorMessage = UNEXPECTED_ERROR_MESSAGE, id, item, maxHeight = '100%', showCollapse, pdfViewerLink, onClick, onCollapse, }) => {
|
|
16
|
+
const [url, setUrl] = useState();
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
(async () => {
|
|
19
|
+
if (fileUrl) {
|
|
20
|
+
setUrl(fileUrl);
|
|
21
|
+
}
|
|
22
|
+
else if (content) {
|
|
23
|
+
// Build a URL from the file
|
|
24
|
+
const urlFromContent = URL.createObjectURL(content);
|
|
25
|
+
if (urlFromContent) {
|
|
26
|
+
setUrl(urlFromContent);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
setUrl(Errors.BlobURL);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return () => {
|
|
33
|
+
if (content && url) {
|
|
34
|
+
URL.revokeObjectURL(url);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
// does not include url to avoid infinite loop
|
|
39
|
+
}, [content, fileUrl]);
|
|
40
|
+
if (!url) {
|
|
41
|
+
return (_jsx(Skeleton, { variant: 'rectangular', width: '100%', height: maxHeight || SCREEN_MAX_HEIGHT }));
|
|
42
|
+
}
|
|
43
|
+
if (url === Errors.BlobURL) {
|
|
44
|
+
return _jsx(Alert, { severity: 'error', children: errorMessage });
|
|
45
|
+
}
|
|
46
|
+
const getComponent = () => {
|
|
47
|
+
const fileExtra = item.type === ItemType.LOCAL_FILE ? getFileExtra(item.extra) : undefined;
|
|
48
|
+
const s3FileExtra = item.type === ItemType.S3_FILE ? getS3FileExtra(item.extra) : undefined;
|
|
49
|
+
const { mimetype, altText, size } = { ...fileExtra, ...s3FileExtra };
|
|
50
|
+
if (mimetype) {
|
|
51
|
+
if (MimeTypes.isImage(mimetype)) {
|
|
52
|
+
return (
|
|
53
|
+
/* The box prevent the image to take full available space due to the stack */
|
|
54
|
+
_jsx(Box, { children: _jsx(FileImage, { id: id, url: url, alt: altText || item.name }) }));
|
|
55
|
+
}
|
|
56
|
+
else if (MimeTypes.isAudio(mimetype)) {
|
|
57
|
+
return _jsx(FileAudio, { id: id, url: url, type: mimetype });
|
|
58
|
+
}
|
|
59
|
+
else if (MimeTypes.isVideo(mimetype)) {
|
|
60
|
+
// does not specify mimetype in video source, this way, it works with more container formats in more browsers (especially Chrome with video/quicktime)
|
|
61
|
+
return _jsx(FileVideo, { id: id, url: url });
|
|
62
|
+
}
|
|
63
|
+
else if (MimeTypes.isPdf(mimetype)) {
|
|
64
|
+
return (_jsx(FilePdf, { id: id, url: url, height: maxHeight, showCollapse: showCollapse, pdfViewerLink: pdfViewerLink }));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (defaultItem) {
|
|
68
|
+
return defaultItem;
|
|
69
|
+
}
|
|
70
|
+
return (_jsx(DownloadButtonFileItem, { id: id, name: item.name, caption: size ? formatFileSize(size) : undefined, url: url, onClick: onClick }));
|
|
71
|
+
};
|
|
72
|
+
let fileItem = getComponent();
|
|
73
|
+
fileItem = (_jsx(SizingWrapper, { size: item.settings.maxWidth, children: fileItem }));
|
|
74
|
+
// display element with caption
|
|
75
|
+
fileItem = _jsx(CaptionWrapper, { item: item, children: fileItem });
|
|
76
|
+
if (showCollapse) {
|
|
77
|
+
fileItem = withCollapse({ item, onCollapse })(fileItem);
|
|
78
|
+
}
|
|
79
|
+
return fileItem;
|
|
80
|
+
};
|
|
81
|
+
export default memo(FileItem);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SxProps } from '@mui/material';
|
|
2
|
+
type FilePdfProps = {
|
|
3
|
+
id?: string;
|
|
4
|
+
url: string;
|
|
5
|
+
height?: number | string;
|
|
6
|
+
sx?: SxProps;
|
|
7
|
+
showCollapse?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* use a custom pdf reader from the link if defined
|
|
10
|
+
* */
|
|
11
|
+
pdfViewerLink?: string;
|
|
12
|
+
};
|
|
13
|
+
declare const FilePdf: ({ url, id, sx, height: defaultHeight, showCollapse, pdfViewerLink, }: FilePdfProps) => JSX.Element;
|
|
14
|
+
export default FilePdf;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { styled } from '@mui/material';
|
|
3
|
+
import { useRef, useState } from 'react';
|
|
4
|
+
import { ITEM_MAX_HEIGHT } from './constants.js';
|
|
5
|
+
const StyledEmbed = styled('embed')({
|
|
6
|
+
maxHeight: ITEM_MAX_HEIGHT,
|
|
7
|
+
});
|
|
8
|
+
const FilePdf = ({ url, id, sx, height: defaultHeight, showCollapse, pdfViewerLink, }) => {
|
|
9
|
+
const embedRef = useRef(null);
|
|
10
|
+
const [height, setHeight] = useState(defaultHeight ?? '100%');
|
|
11
|
+
const onLoad = (e) => {
|
|
12
|
+
// only set pdf height if not using collapse
|
|
13
|
+
if (!showCollapse) {
|
|
14
|
+
// set pdf height -> probably very high
|
|
15
|
+
const newHeight = e.target?.offsetParent
|
|
16
|
+
?.scrollHeight;
|
|
17
|
+
if (newHeight) {
|
|
18
|
+
setHeight(newHeight);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
// use custom pdf viewer if defined
|
|
23
|
+
let urlWithPdfViewer = url;
|
|
24
|
+
if (pdfViewerLink) {
|
|
25
|
+
urlWithPdfViewer = `${pdfViewerLink}${encodeURIComponent(url)}`;
|
|
26
|
+
}
|
|
27
|
+
return (_jsx(StyledEmbed, { ref: embedRef, id: id, src: urlWithPdfViewer, width: '100%', height: height || '100%', onLoad: onLoad, sx: sx }));
|
|
28
|
+
};
|
|
29
|
+
export default FilePdf;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { styled } from '@mui/material';
|
|
3
|
+
const StyledVideo = styled('video')({
|
|
4
|
+
maxWidth: '100%',
|
|
5
|
+
});
|
|
6
|
+
const FileVideo = ({ id, url, sx }) => {
|
|
7
|
+
return (_jsx(StyledVideo, { sx: sx, id: id, controls: true, children: _jsx("source", { src: url }) }));
|
|
8
|
+
};
|
|
9
|
+
export default FileVideo;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React props types for {@link H5PItem}
|
|
3
|
+
*/
|
|
4
|
+
type H5PItemProps = {
|
|
5
|
+
itemName: string;
|
|
6
|
+
itemId: string;
|
|
7
|
+
contentId: string;
|
|
8
|
+
integrationUrl: string;
|
|
9
|
+
iframeId?: string;
|
|
10
|
+
showCollapse?: boolean;
|
|
11
|
+
onCollapse?: (c: boolean) => void;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The H5PItem component displays an iframe with the content of an H5P
|
|
15
|
+
*
|
|
16
|
+
* This component bridges the gap between the procedural "h5p-standalone"
|
|
17
|
+
* package and the Graasp React ecosystem
|
|
18
|
+
*/
|
|
19
|
+
declare const H5PItem: ({ itemName, itemId, contentId, integrationUrl: integrationBase, iframeId, showCollapse, onCollapse, }: H5PItemProps) => JSX.Element;
|
|
20
|
+
export default H5PItem;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { validate, version } from 'uuid';
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
|
+
import withCollapse from '../Collapse/withCollapse.js';
|
|
5
|
+
/**
|
|
6
|
+
* The H5PItem component displays an iframe with the content of an H5P
|
|
7
|
+
*
|
|
8
|
+
* This component bridges the gap between the procedural "h5p-standalone"
|
|
9
|
+
* package and the Graasp React ecosystem
|
|
10
|
+
*/
|
|
11
|
+
const H5PItem = ({ itemName, itemId, contentId, integrationUrl: integrationBase, iframeId = `h5p-container-${itemId}`, showCollapse = false, onCollapse, }) => {
|
|
12
|
+
/*
|
|
13
|
+
h5p-standalone (and H5P itself) expect the integration to be done on the
|
|
14
|
+
window global object, which does not allow multiple H5Ps to be loaded
|
|
15
|
+
simultaneously (as they will be competing for the same global object)
|
|
16
|
+
As a workaround, we wrap the H5P integration into an iframe, such that it
|
|
17
|
+
gets its own window object. We can also enable the sandbox attribute for
|
|
18
|
+
additional security
|
|
19
|
+
*/
|
|
20
|
+
const integrationUrl = new URL(integrationBase);
|
|
21
|
+
integrationUrl.searchParams.set('content', encodeURIComponent(contentId));
|
|
22
|
+
const iframeRef = useRef(null);
|
|
23
|
+
// Listen for content height changes
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const onResize = (event) => {
|
|
26
|
+
// iframe must be mounted
|
|
27
|
+
if (iframeRef.current === null) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// message origin must be same window
|
|
31
|
+
if (event.origin !== integrationUrl.origin) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// message source must be iframe of this H5P integration
|
|
35
|
+
if (event.source !== iframeRef.current.contentWindow) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// message data should be object
|
|
39
|
+
if (!event.data || typeof event.data !== 'object') {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// message should have fields contentId and height
|
|
43
|
+
if (!event.data.contentId || !event.data.height) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// contentId should be UUID string
|
|
47
|
+
if (typeof event.data.contentId !== 'string' ||
|
|
48
|
+
version(event.data.contentId) !== 4 ||
|
|
49
|
+
!validate(event.data.contentId)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// contentId should match current item
|
|
53
|
+
if (event.data.contentId !== contentId) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// height should be number
|
|
57
|
+
if (typeof event.data.height !== 'number') {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// height should be int
|
|
61
|
+
const newHeight = parseInt(event.data.height);
|
|
62
|
+
iframeRef.current.height = newHeight.toString();
|
|
63
|
+
};
|
|
64
|
+
window.addEventListener('message', onResize);
|
|
65
|
+
// cleanup on unmount
|
|
66
|
+
return () => {
|
|
67
|
+
window.removeEventListener('message', onResize);
|
|
68
|
+
};
|
|
69
|
+
}, []);
|
|
70
|
+
let iframeH5Pitem = (_jsx("iframe", { ref: iframeRef, id: iframeId, src: integrationUrl.href, allowFullScreen: true, scrolling: 'no', style: {
|
|
71
|
+
width: '100%',
|
|
72
|
+
border: 'none',
|
|
73
|
+
display: 'block',
|
|
74
|
+
} }));
|
|
75
|
+
if (showCollapse) {
|
|
76
|
+
iframeH5Pitem = withCollapse({ item: { name: itemName }, onCollapse })(iframeH5Pitem);
|
|
77
|
+
}
|
|
78
|
+
return iframeH5Pitem;
|
|
79
|
+
};
|
|
80
|
+
export default H5PItem;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DiscriminatedItem } from '@lnco-ai/sdk';
|
|
2
|
+
export type ItemSkeletonProps = {
|
|
3
|
+
/**
|
|
4
|
+
* prevent displaying skeleton if item is a folder
|
|
5
|
+
*/
|
|
6
|
+
isChildren: boolean;
|
|
7
|
+
isCollapsible?: boolean;
|
|
8
|
+
itemType: DiscriminatedItem['type'];
|
|
9
|
+
screenMaxHeight?: number;
|
|
10
|
+
};
|
|
11
|
+
declare const ItemSkeleton: ({ isChildren, isCollapsible, itemType, screenMaxHeight, }: ItemSkeletonProps) => JSX.Element | null;
|
|
12
|
+
export default ItemSkeleton;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Skeleton } from '@mui/material';
|
|
3
|
+
import { ItemType } from '@lnco-ai/sdk';
|
|
4
|
+
import { SCREEN_MAX_HEIGHT } from '../../constants.js';
|
|
5
|
+
const SKELETON_COLLAPSE_HEIGHT = '3.5em';
|
|
6
|
+
const SKELETON_FOLDER_BUTTON_HEIGHT = '8.125em';
|
|
7
|
+
const ItemSkeleton = ({ isChildren, isCollapsible, itemType, screenMaxHeight, }) => {
|
|
8
|
+
switch (true) {
|
|
9
|
+
case isCollapsible: {
|
|
10
|
+
return (_jsx(Skeleton, { variant: 'rectangular', width: '100%', height: SKELETON_COLLAPSE_HEIGHT }));
|
|
11
|
+
}
|
|
12
|
+
case itemType === ItemType.FOLDER && isChildren: {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
case itemType === ItemType.FOLDER: {
|
|
16
|
+
return (_jsx(Skeleton, { variant: 'rectangular', width: '100%', height: SKELETON_FOLDER_BUTTON_HEIGHT }));
|
|
17
|
+
}
|
|
18
|
+
case [
|
|
19
|
+
ItemType.LOCAL_FILE,
|
|
20
|
+
ItemType.S3_FILE,
|
|
21
|
+
ItemType.LINK,
|
|
22
|
+
ItemType.APP,
|
|
23
|
+
].includes(itemType):
|
|
24
|
+
{
|
|
25
|
+
return (_jsx(Skeleton, { variant: 'rectangular', width: '100%', height: screenMaxHeight || SCREEN_MAX_HEIGHT }));
|
|
26
|
+
}
|
|
27
|
+
case itemType === ItemType.DOCUMENT: {
|
|
28
|
+
return (_jsxs(_Fragment, { children: [_jsx(Skeleton, { variant: 'text' }), _jsx(Skeleton, { variant: 'text' }), _jsx(Skeleton, { variant: 'text' })] }));
|
|
29
|
+
}
|
|
30
|
+
default: {
|
|
31
|
+
return _jsx(Skeleton, { variant: 'rectangular', width: '100%' });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
export default ItemSkeleton;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { LinkItemType } from '@lnco-ai/sdk';
|
|
2
|
+
type LinkItemProps = {
|
|
3
|
+
/**
|
|
4
|
+
* Id of the component used for testing
|
|
5
|
+
*/
|
|
6
|
+
id?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Id of the current member used for saving the resizing preferences
|
|
9
|
+
*/
|
|
10
|
+
memberId?: string;
|
|
11
|
+
errorMessage?: string;
|
|
12
|
+
height?: number | string;
|
|
13
|
+
/**
|
|
14
|
+
* whether the link can be resized
|
|
15
|
+
*/
|
|
16
|
+
isResizable?: boolean;
|
|
17
|
+
item: LinkItemType;
|
|
18
|
+
/**
|
|
19
|
+
* Thumbnail url of the item
|
|
20
|
+
*/
|
|
21
|
+
thumbnail?: string;
|
|
22
|
+
loadingMessage?: string;
|
|
23
|
+
/**
|
|
24
|
+
* whether the caption should be displayed
|
|
25
|
+
*/
|
|
26
|
+
showCaption?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* whether the iframe should be displayed
|
|
29
|
+
*/
|
|
30
|
+
showIframe?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* whether the button should be displayed
|
|
33
|
+
*/
|
|
34
|
+
showButton?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* whether the component should be collapse
|
|
37
|
+
*/
|
|
38
|
+
showCollapse?: boolean;
|
|
39
|
+
onClick?: () => void;
|
|
40
|
+
onCollapse?: (c: boolean) => void;
|
|
41
|
+
};
|
|
42
|
+
declare const _default: import("react").MemoExoticComponent<({ id, item, thumbnail, memberId, showCaption, showIframe, showButton, loadingMessage, height: defaultHeight, errorMessage, isResizable, showCollapse, onClick, onCollapse, }: LinkItemProps) => JSX.Element>;
|
|
43
|
+
export default _default;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Alert, Box, Link as MUILink, styled } from '@mui/material';
|
|
3
|
+
import { Fragment, memo, useEffect, useState } from 'react';
|
|
4
|
+
import { getLinkExtra } from '@lnco-ai/sdk';
|
|
5
|
+
import LinkCard from '../Card/LinkCard.js';
|
|
6
|
+
import withCollapse from '../Collapse/withCollapse.js';
|
|
7
|
+
import { DEFAULT_LINK_SHOW_BUTTON } from '../constants.js';
|
|
8
|
+
import { ITEM_MAX_HEIGHT } from './constants.js';
|
|
9
|
+
import { iframeCommonStyles } from './iframeStyles.js';
|
|
10
|
+
import withCaption from './withCaption.js';
|
|
11
|
+
import withResizing from './withResizing.js';
|
|
12
|
+
const StyledIFrame = styled('iframe')(({ isResizable, height }) => ({
|
|
13
|
+
...iframeCommonStyles,
|
|
14
|
+
maxHeight: !isResizable ? ITEM_MAX_HEIGHT : undefined,
|
|
15
|
+
height: !isResizable ? height : '100%',
|
|
16
|
+
}));
|
|
17
|
+
function isURLExternal(url) {
|
|
18
|
+
try {
|
|
19
|
+
return new URL(url).origin !== window.location.origin;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const IFrameContainer = styled('div')(({ theme }) => ({
|
|
26
|
+
width: '100%',
|
|
27
|
+
backgroundColor: '#eee',
|
|
28
|
+
borderRadius: theme.spacing(2),
|
|
29
|
+
display: 'flex',
|
|
30
|
+
alignItems: 'center',
|
|
31
|
+
justifyContent: 'center',
|
|
32
|
+
flexGrow: 1,
|
|
33
|
+
height: '100%',
|
|
34
|
+
maxHeight: ITEM_MAX_HEIGHT,
|
|
35
|
+
overflow: 'auto',
|
|
36
|
+
}));
|
|
37
|
+
const LinkIframe = ({ id, url, title, height, isResizable, isLoading, onDoneLoading, itemId, memberId, loadingMessage, }) => {
|
|
38
|
+
const iframe = (_jsx(StyledIFrame, { sx: { display: isLoading ? 'unset' : 'block' }, height: height, width: '100%', id: id, isResizable: isResizable, onLoad: onDoneLoading, src: url, title: title }));
|
|
39
|
+
const ResizableLink = withResizing({
|
|
40
|
+
height,
|
|
41
|
+
component: iframe,
|
|
42
|
+
memberId,
|
|
43
|
+
itemId,
|
|
44
|
+
});
|
|
45
|
+
return (_jsxs(_Fragment, { children: [_jsx(IFrameContainer, { sx: { display: isLoading ? 'flex' : 'none' }, children: loadingMessage }), isResizable ? _jsx(ResizableLink, {}) : iframe] }));
|
|
46
|
+
};
|
|
47
|
+
const LinkItem = ({ id, item, thumbnail, memberId, showCaption = true, showIframe = false, showButton = DEFAULT_LINK_SHOW_BUTTON, loadingMessage = 'Link is Loading...', height: defaultHeight = 400, errorMessage = 'The link is malformed.', isResizable = false, showCollapse = false, onClick, onCollapse, }) => {
|
|
48
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
49
|
+
const [height] = useState(defaultHeight);
|
|
50
|
+
const { id: itemId, name } = item;
|
|
51
|
+
const extra = getLinkExtra(item.extra);
|
|
52
|
+
const html = extra?.html;
|
|
53
|
+
// default case is an iframe with given link
|
|
54
|
+
const url = extra?.url;
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (!isLoading) {
|
|
57
|
+
setIsLoading(true);
|
|
58
|
+
}
|
|
59
|
+
}, [url]);
|
|
60
|
+
const CaptionWrapper = withCaption({
|
|
61
|
+
item,
|
|
62
|
+
});
|
|
63
|
+
const getComponent = () => {
|
|
64
|
+
if (!url) {
|
|
65
|
+
return _jsx(Alert, { severity: 'error', children: errorMessage });
|
|
66
|
+
}
|
|
67
|
+
const isExternal = isURLExternal(url);
|
|
68
|
+
const linkCard = (_jsx(LinkCard, { id: id, thumbnail: thumbnail ?? item.extra.embeddedLink.icons?.[0], title: item.name, url: url, description: item.extra.embeddedLink.description ?? '', onClick: onClick, isExternal: isExternal }));
|
|
69
|
+
if (showIframe) {
|
|
70
|
+
// for rich media we use the provided html
|
|
71
|
+
// this is highly unsafe, and could allow XSS vulnerability if the backend does not protect this property
|
|
72
|
+
if (html) {
|
|
73
|
+
return (_jsx(Box
|
|
74
|
+
// this is allows for the box to not really exist and instead display the children box
|
|
75
|
+
// we can not get rid of this div as we need a way to attach the onClick handler for registering actions
|
|
76
|
+
, {
|
|
77
|
+
// this is allows for the box to not really exist and instead display the children box
|
|
78
|
+
// we can not get rid of this div as we need a way to attach the onClick handler for registering actions
|
|
79
|
+
display: 'contents', id: id, onClick: onClick, dangerouslySetInnerHTML: { __html: html } }));
|
|
80
|
+
}
|
|
81
|
+
return (_jsxs(Fragment, { children: [_jsx(LinkIframe, { id: id, url: url, isResizable: isResizable, height: height, title: name, isLoading: isLoading, onDoneLoading: () => setIsLoading(false), itemId: itemId, memberId: memberId, loadingMessage: loadingMessage }), showButton && linkCard] }));
|
|
82
|
+
}
|
|
83
|
+
if (showButton) {
|
|
84
|
+
return linkCard;
|
|
85
|
+
}
|
|
86
|
+
return (_jsx(MUILink, { href: url, onClick: onClick, children: url }));
|
|
87
|
+
};
|
|
88
|
+
let linkItem = getComponent();
|
|
89
|
+
if (showCaption) {
|
|
90
|
+
linkItem = CaptionWrapper(linkItem);
|
|
91
|
+
}
|
|
92
|
+
if (showCollapse) {
|
|
93
|
+
linkItem = withCollapse({ item: { name }, onCollapse })(linkItem);
|
|
94
|
+
}
|
|
95
|
+
return linkItem;
|
|
96
|
+
};
|
|
97
|
+
export default memo(LinkItem);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from '@mui/material';
|
|
3
|
+
import { MaxWidth } from '@lnco-ai/sdk';
|
|
4
|
+
const getWidthFromSizing = (size) => {
|
|
5
|
+
switch (size) {
|
|
6
|
+
case MaxWidth.ExtraSmall:
|
|
7
|
+
return '100px';
|
|
8
|
+
case MaxWidth.Small:
|
|
9
|
+
return '200px';
|
|
10
|
+
case MaxWidth.Medium:
|
|
11
|
+
return '400px';
|
|
12
|
+
case MaxWidth.Large:
|
|
13
|
+
return '800px';
|
|
14
|
+
// default is for the element to take all available horizontal space
|
|
15
|
+
case MaxWidth.ExtraLarge:
|
|
16
|
+
default:
|
|
17
|
+
return '100%';
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
export const SizingWrapper = ({ size = MaxWidth.ExtraLarge, children, }) => {
|
|
21
|
+
const width = getWidthFromSizing(size);
|
|
22
|
+
return (_jsx(Box, { maxWidth: '100%', width: width, children: children }));
|
|
23
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { DiscriminatedItem, LocalContext, UUID } from '@lnco-ai/sdk';
|
|
2
|
+
export type Token = string;
|
|
3
|
+
declare const CALIBRATION_FONT_SIZES: readonly ["small", "normal", "large", "extra-large"];
|
|
4
|
+
type CalibrationFontSize = (typeof CALIBRATION_FONT_SIZES)[number];
|
|
5
|
+
type ScreenCalibration = {
|
|
6
|
+
scale?: number;
|
|
7
|
+
fontSize?: CalibrationFontSize;
|
|
8
|
+
participantId?: string;
|
|
9
|
+
participantCode?: string;
|
|
10
|
+
};
|
|
11
|
+
export type ContextPayload = Pick<LocalContext, 'apiHost' | 'itemId' | 'settings' | 'permission' | 'lang' | 'context' | 'accountId'> & {
|
|
12
|
+
screenCalibration?: ScreenCalibration;
|
|
13
|
+
};
|
|
14
|
+
declare const useAppCommunication: ({ item, contextPayload, appUrl, iFrameRef, requestApiAccessToken, }: {
|
|
15
|
+
item: DiscriminatedItem;
|
|
16
|
+
appUrl: string;
|
|
17
|
+
contextPayload: ContextPayload;
|
|
18
|
+
iFrameRef: React.RefObject<HTMLIFrameElement>;
|
|
19
|
+
requestApiAccessToken: (payload: {
|
|
20
|
+
id: UUID;
|
|
21
|
+
key: string;
|
|
22
|
+
origin: string;
|
|
23
|
+
}) => Promise<{
|
|
24
|
+
token: Token;
|
|
25
|
+
}>;
|
|
26
|
+
}) => void;
|
|
27
|
+
export { useAppCommunication };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
const CALIBRATION_FONT_SIZES = [
|
|
3
|
+
'small',
|
|
4
|
+
'normal',
|
|
5
|
+
'large',
|
|
6
|
+
'extra-large',
|
|
7
|
+
];
|
|
8
|
+
const isCalibrationFontSize = (value) => typeof value === 'string' &&
|
|
9
|
+
CALIBRATION_FONT_SIZES.includes(value);
|
|
10
|
+
const buildPostMessageKeys = (itemId) => ({
|
|
11
|
+
GET_CONTEXT_SUCCESS: `GET_CONTEXT_SUCCESS_${itemId}`,
|
|
12
|
+
GET_CONTEXT_FAILURE: `GET_CONTEXT_FAILURE_${itemId}`,
|
|
13
|
+
GET_CONTEXT: `GET_CONTEXT_${itemId}`,
|
|
14
|
+
GET_AUTH_TOKEN: `GET_AUTH_TOKEN_${itemId}`,
|
|
15
|
+
GET_AUTH_TOKEN_SUCCESS: `GET_AUTH_TOKEN_SUCCESS_${itemId}`,
|
|
16
|
+
GET_AUTH_TOKEN_FAILURE: `GET_AUTH_TOKEN_FAILURE_${itemId}`,
|
|
17
|
+
POST_AUTO_RESIZE: `POST_AUTO_RESIZE_${itemId}`,
|
|
18
|
+
POST_CALIBRATION_SCALE: `POST_CALIBRATION_SCALE_${itemId}`,
|
|
19
|
+
});
|
|
20
|
+
const useAppCommunication = ({ item, contextPayload, appUrl, iFrameRef, requestApiAccessToken, }) => {
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
// receive message from app through MessageChannel
|
|
23
|
+
const setupOnMessage = (port) => async (e) => {
|
|
24
|
+
const { data, origin: requestOrigin } = e;
|
|
25
|
+
const POST_MESSAGE_KEYS = buildPostMessageKeys(item.id);
|
|
26
|
+
// responds only to corresponding app
|
|
27
|
+
if (!appUrl?.includes(requestOrigin)) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const { type, payload } = JSON.parse(data);
|
|
31
|
+
switch (type) {
|
|
32
|
+
case POST_MESSAGE_KEYS.GET_AUTH_TOKEN: {
|
|
33
|
+
port.postMessage(JSON.stringify({
|
|
34
|
+
type: POST_MESSAGE_KEYS.GET_AUTH_TOKEN_SUCCESS,
|
|
35
|
+
payload: await requestApiAccessToken({
|
|
36
|
+
id: item.id,
|
|
37
|
+
...payload,
|
|
38
|
+
}),
|
|
39
|
+
}));
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
case POST_MESSAGE_KEYS.POST_AUTO_RESIZE: {
|
|
43
|
+
// item should not be manually resizable
|
|
44
|
+
if (item.settings.isResizable) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// iframe must be mounted
|
|
48
|
+
if (iFrameRef.current === null) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
// payload should be number
|
|
52
|
+
if (typeof payload !== 'number') {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
iFrameRef.current.height = payload.toString();
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case POST_MESSAGE_KEYS.POST_CALIBRATION_SCALE: {
|
|
59
|
+
const { scale, fontSize, participantId, participantCode } = payload?.screenCalibration ?? {};
|
|
60
|
+
if (scale === undefined && fontSize === undefined)
|
|
61
|
+
return;
|
|
62
|
+
if (scale !== undefined &&
|
|
63
|
+
(typeof scale !== 'number' ||
|
|
64
|
+
Number.isNaN(scale) ||
|
|
65
|
+
scale <= 0.5 ||
|
|
66
|
+
scale >= 3))
|
|
67
|
+
return;
|
|
68
|
+
if (fontSize !== undefined && !isCalibrationFontSize(fontSize))
|
|
69
|
+
return;
|
|
70
|
+
try {
|
|
71
|
+
localStorage.setItem('lnco_screen_calibration', JSON.stringify({
|
|
72
|
+
screenCalibration: {
|
|
73
|
+
...(scale !== undefined && { scale }),
|
|
74
|
+
...(fontSize !== undefined && { fontSize }),
|
|
75
|
+
...(participantId !== undefined && { participantId }),
|
|
76
|
+
...(participantCode !== undefined && { participantCode }),
|
|
77
|
+
},
|
|
78
|
+
timestamp: Date.now(),
|
|
79
|
+
calibrationAppId: item.id,
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const windowOnMessage = (e) => {
|
|
90
|
+
const { data, origin: requestOrigin } = e;
|
|
91
|
+
const POST_MESSAGE_KEYS = buildPostMessageKeys(item.id);
|
|
92
|
+
// responds only to corresponding app
|
|
93
|
+
if (!appUrl?.includes(requestOrigin)) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// return context data and message channel port to app
|
|
97
|
+
const { type } = JSON.parse(data);
|
|
98
|
+
if (type === POST_MESSAGE_KEYS.GET_CONTEXT) {
|
|
99
|
+
// create/reset channel and
|
|
100
|
+
// Listen for messages on port1
|
|
101
|
+
const channel = new MessageChannel();
|
|
102
|
+
const { port1 } = channel;
|
|
103
|
+
port1.onmessage = setupOnMessage(port1);
|
|
104
|
+
// ensure to only send the message to the domain where the app is hosted for security reasons
|
|
105
|
+
const targetOrigin = new URL(appUrl).origin;
|
|
106
|
+
// Transfer port2 to the iframe
|
|
107
|
+
// provide port2 to app and item's data
|
|
108
|
+
iFrameRef?.current?.contentWindow?.postMessage(JSON.stringify({
|
|
109
|
+
type: POST_MESSAGE_KEYS.GET_CONTEXT_SUCCESS,
|
|
110
|
+
payload: {
|
|
111
|
+
/**
|
|
112
|
+
* Legacy for old apps or apps that does not use apps-query-client
|
|
113
|
+
*/
|
|
114
|
+
memberId: contextPayload.accountId,
|
|
115
|
+
...contextPayload,
|
|
116
|
+
},
|
|
117
|
+
}), targetOrigin, [channel.port2]);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
window.addEventListener('message', windowOnMessage);
|
|
121
|
+
return () => window.removeEventListener('message', windowOnMessage);
|
|
122
|
+
}, [item, iFrameRef]);
|
|
123
|
+
};
|
|
124
|
+
export { useAppCommunication };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ITEM_MAX_HEIGHT = "70vh";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const ITEM_MAX_HEIGHT = '70vh';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as AppItem } from './AppItem.js';
|
|
2
|
+
export { default as EtherpadItem } from './EtherpadItem.js';
|
|
3
|
+
export { default as FileItem } from './FileItem.js';
|
|
4
|
+
export { default as H5PItem } from './H5PItem.js';
|
|
5
|
+
export { default as LinkItem } from './LinkItem.js';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as AppItem } from './AppItem.js';
|
|
2
|
+
export { default as EtherpadItem } from './EtherpadItem.js';
|
|
3
|
+
export { default as FileItem } from './FileItem.js';
|
|
4
|
+
export { default as H5PItem } from './H5PItem.js';
|
|
5
|
+
export { default as LinkItem } from './LinkItem.js';
|