122 lines
3.0 KiB
TypeScript
122 lines
3.0 KiB
TypeScript
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
|
|
import { useTheme } from '@emotion/react';
|
|
import styled from '@emotion/styled';
|
|
import { useState } from 'react';
|
|
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
|
|
|
|
const StyledContainer = styled.div`
|
|
background: ${({ theme }) => theme.background.transparent.lighter};
|
|
border-radius: ${({ theme }) => theme.border.radius.md};
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
position: relative;
|
|
width: 100%;
|
|
`;
|
|
|
|
const StyledIframe = styled.iframe`
|
|
border: none;
|
|
flex: 1;
|
|
height: 100%;
|
|
width: 100%;
|
|
`;
|
|
|
|
const StyledLoadingContainer = styled.div`
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
padding-top: ${({ theme }) => theme.spacing(2)};
|
|
padding-left: ${({ theme }) => theme.spacing(2)};
|
|
background: ${({ theme }) => theme.background.primary};
|
|
pointer-events: none;
|
|
z-index: 1;
|
|
`;
|
|
|
|
const StyledErrorContainer = styled.div`
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100%;
|
|
padding: ${({ theme }) => theme.spacing(4)};
|
|
text-align: center;
|
|
`;
|
|
|
|
const StyledErrorMessage = styled.div`
|
|
color: ${({ theme }) => theme.font.color.secondary};
|
|
font-size: ${({ theme }) => theme.font.size.md};
|
|
margin-top: ${({ theme }) => theme.spacing(2)};
|
|
`;
|
|
|
|
const StyledErrorUrl = styled.div`
|
|
color: ${({ theme }) => theme.font.color.tertiary};
|
|
font-size: ${({ theme }) => theme.font.size.sm};
|
|
margin-top: ${({ theme }) => theme.spacing(1)};
|
|
word-break: break-all;
|
|
`;
|
|
|
|
export type IframeWidgetProps = {
|
|
url: string;
|
|
title?: string;
|
|
};
|
|
|
|
export const IframeWidget = ({
|
|
url,
|
|
title = 'Embedded Content',
|
|
}: IframeWidgetProps) => {
|
|
const theme = useTheme();
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [hasError, setHasError] = useState(false);
|
|
|
|
const handleIframeLoad = () => {
|
|
setIsLoading(false);
|
|
};
|
|
|
|
const handleIframeError = () => {
|
|
setIsLoading(false);
|
|
setHasError(true);
|
|
};
|
|
|
|
if (hasError) {
|
|
return (
|
|
<StyledContainer>
|
|
<StyledErrorContainer>
|
|
<StyledErrorMessage>Failed to load content</StyledErrorMessage>
|
|
<StyledErrorUrl>{url}</StyledErrorUrl>
|
|
</StyledErrorContainer>
|
|
</StyledContainer>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<StyledContainer>
|
|
{isLoading && (
|
|
<StyledLoadingContainer>
|
|
<SkeletonTheme
|
|
baseColor={theme.background.tertiary}
|
|
highlightColor={theme.background.transparent.lighter}
|
|
borderRadius={4}
|
|
>
|
|
<Skeleton
|
|
width={120}
|
|
height={SKELETON_LOADER_HEIGHT_SIZES.standard.m}
|
|
/>
|
|
</SkeletonTheme>
|
|
</StyledLoadingContainer>
|
|
)}
|
|
<StyledIframe
|
|
src={url}
|
|
title={title}
|
|
onLoad={handleIframeLoad}
|
|
onError={handleIframeError}
|
|
sandbox="allow-scripts allow-forms allow-popups"
|
|
allow="encrypted-media"
|
|
allowFullScreen
|
|
/>
|
|
</StyledContainer>
|
|
);
|
|
};
|