Untitled
unknown
plain_text
9 months ago
12 kB
7
Indexable
import Tab from '@mui/material/Tab';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { TabContext, TabList, TabPanel, TabPanelProps } from '@mui/lab';
import { styled, useTheme } from '@mui/material/styles';
import ItemTextViewer from './ItemTextViewer';
import ItemNativeViewer from './ItemNativeViewer';
import FooterSpacer from '../common/FooterSpacer';
import ItemPdfViewer from './ItemPdfViewer';
import ItemApi from '../../api/ItemApi';
import { handleError } from '../../utils/errorUtils';
import CONSTANTS from '../common/constants';
import FaintInformational from '../common/FaintInformational';
import Informational from '../common/Informational';
import { Box } from '@mui/material';
import { useItemsContext } from '../item/context/ItemsContext';
import { getViewerTypeForItemDefinitionId } from './ItemViewerUtils';
import { useCasesAppContext } from '../case/CasesAppContext';
import { useResizeObserver } from '../../hooks';
import { useMutation, useQuery } from '@tanstack/react-query';
export type ItemArtifact = {
id: number;
itemId: number;
artifactType: string;
extension: string;
path: string;
};
type ItemViewerProps = {
itemId: number;
itemDefinitionId: number;
isReOrientatedHorizontally: boolean;
};
const ItemViewer: React.FC<ItemViewerProps> = ({
itemId,
itemDefinitionId,
isReOrientatedHorizontally,
}) => {
const { caseId } = useCasesAppContext();
const { isPending, data, error } = useItemArtifacts(itemId, caseId);
if (error) {
handleError(
error,
'Failed to retrieve item artifacts types, defaulting to Original',
);
}
const itemArtifacts = (data ?? []) as ItemArtifact[];
const hasArtifacts = itemArtifacts.length > 0;
const hasMarkupArtifact = itemArtifacts.some(
({ artifactType }) => artifactType === CONSTANTS.ARTIFACT_TYPE.MARKUP,
);
const artifactsToTabs = useMemo(
() =>
itemArtifacts.map((artifact) =>
artifact.artifactType === CONSTANTS.ARTIFACT_TYPE.WEB_NATIVE
? CONSTANTS.ITEM_VIEWER_TAB_TYPE.ORIGINAL
: (artifact.artifactType as TabType),
),
[itemArtifacts],
);
const { itemDefinitions, itemKinds } = useItemsContext();
const [containerRef, scrollButtons] = useScrollButtonsOnResize();
const [preferredTab, setPreferredTab] = useState<TabType | null>(null);
const handleTabChange = (_: React.SyntheticEvent, newValue: TabType) => {
setPreferredTab(newValue);
};
const activeTab = useMemo(() => {
// Allow users to create Markup on the fly if it doesn't exits
if (
(preferredTab && artifactsToTabs.includes(preferredTab)) ||
preferredTab === CONSTANTS.ITEM_VIEWER_TAB_TYPE.MARKUP
)
return preferredTab;
if (artifactsToTabs.includes(CONSTANTS.ITEM_VIEWER_TAB_TYPE.PDF))
return CONSTANTS.ITEM_VIEWER_TAB_TYPE.PDF;
if (artifactsToTabs.includes(CONSTANTS.ITEM_VIEWER_TAB_TYPE.ORIGINAL))
return CONSTANTS.ITEM_VIEWER_TAB_TYPE.ORIGINAL;
if (artifactsToTabs.includes(CONSTANTS.ITEM_VIEWER_TAB_TYPE.TEXT))
return CONSTANTS.ITEM_VIEWER_TAB_TYPE.TEXT;
// if (artifactsToTabs.includes(CONSTANTS.ITEM_VIEWER_TAB_TYPE.MARKUP))
// return CONSTANTS.ITEM_VIEWER_TAB_TYPE.MARKUP;
return CONSTANTS.ITEM_VIEWER_TAB_TYPE.PDF;
}, [preferredTab, itemArtifacts]);
const isNativePdfViewer = useMemo(() => {
const viewers = getViewerTypeForItemDefinitionId(
itemDefinitionId,
itemDefinitions,
itemKinds,
);
return viewers.Native === CONSTANTS.ITEM_KIND_DOCUMENT;
}, [itemId, itemDefinitionId, itemArtifacts]);
const isPdfTab = activeTab === CONSTANTS.ITEM_VIEWER_TAB_TYPE.PDF;
const isMarkupTab = activeTab === CONSTANTS.ITEM_VIEWER_TAB_TYPE.MARKUP;
const isOriginalTab =
activeTab === CONSTANTS.ITEM_VIEWER_TAB_TYPE.ORIGINAL && isNativePdfViewer;
let loading = isPending;
if (isMarkupTab && !hasMarkupArtifact) {
const {
isPending: isCreateMarkupArtifact,
data: markupArtifact,
error,
} = useCreateItemMarkupArtifact(itemId, caseId, !isPending);
if (error) {
handleError(error, 'Failed to create Markup Artifact');
} else {
itemArtifacts.push(markupArtifact);
}
loading = isCreateMarkupArtifact;
}
const showItemPdfViewerTab = useMemo(() => {
if (!itemId) return false;
if (loading) return false;
if (!hasArtifacts) return false;
return isPdfTab || isMarkupTab || isOriginalTab;
}, [itemId, loading, hasArtifacts, isPdfTab, isMarkupTab, isOriginalTab]);
const pdfViewerArtifactType = useMemo(() => {
if (isPdfTab) return CONSTANTS.ARTIFACT_TYPE.PDF;
if (isMarkupTab) return CONSTANTS.ARTIFACT_TYPE.MARKUP;
return CONSTANTS.ARTIFACT_TYPE.NATIVE;
}, [isPdfTab, isMarkupTab, activeTab]);
return (
<div
id="viewer-outline"
ref={containerRef}
style={{
alignContent: 'center',
display: 'flex',
flexDirection: 'column',
height: '100%',
width: '100%',
borderColor: 'divider',
overflow: 'hidden',
}}
>
<TabContext value={activeTab}>
<ItemViewerTabList
itemId={itemId}
scrollButtons={scrollButtons}
artifactsToTabs={artifactsToTabs}
onChange={handleTabChange}
/>
<ItemViewerTabPanelTabs
itemId={itemId}
itemDefinitionId={itemDefinitionId}
isHorizontal={isReOrientatedHorizontally}
loading={loading}
hasArtifacts={hasArtifacts}
isNativePdfViewer={isNativePdfViewer}
itemArtifacts={itemArtifacts}
/>
</TabContext>
<ItemViewerPdfTabPanel
show={showItemPdfViewerTab}
itemId={itemId}
isHorizontal={isReOrientatedHorizontally}
loading={loading}
itemArtifacts={itemArtifacts}
pdfViewerArtifactType={pdfViewerArtifactType}
editable={isMarkupTab}
/>
{!isReOrientatedHorizontally ? <FooterSpacer /> : null}
</div>
);
};
export default ItemViewer;
type ScrollButton = boolean | 'auto' | undefined;
const useScrollButtonsOnResize = (): [
React.RefObject<HTMLDivElement>,
ScrollButton,
] => {
const ref = useRef<HTMLDivElement>(null);
const [scrollButtons, setScrollButtons] = useState<ScrollButton>(false);
useResizeObserver(ref, () => {
if (ref.current) {
const actualWidth = ref.current.offsetWidth;
if (actualWidth < 300) {
setScrollButtons('auto');
} else {
setScrollButtons(false);
}
}
});
return [ref, scrollButtons];
};
const TabHeader = styled(Tab)({
minHeight: '30px',
height: '30px',
});
type TabContainerProps = TabPanelProps & {
isHorizontal: boolean;
};
const ItemViewerTabPanelContainer = styled(TabPanel, {
shouldForwardProp: (prop) => prop !== 'isHorizontal',
})<TabContainerProps>(({ isHorizontal }) => ({
height: isHorizontal ? '100%' : `calc(100% - 99px)`,
width: '100%',
overflow: 'hidden',
padding: 0,
}));
type TabType = 'Original' | 'Pdf' | 'Text';
const ItemViewerTabList: React.FC<{
itemId: number;
scrollButtons: ScrollButton;
artifactsToTabs: string[];
onChange: (event: React.SyntheticEvent, value: TabType) => void;
}> = ({ itemId, scrollButtons, artifactsToTabs, onChange }) => {
const theme = useTheme();
return (
<TabList
onChange={onChange}
sx={{
width: 'auto',
minHeight: '28px',
height: '28px',
background: theme.palette.gridHeaderRowBackGroundColor.main,
borderBottom: `1px solid ${theme.palette.divider}`,
}}
variant="scrollable"
scrollButtons={scrollButtons}
aria-label="documber viewer tabs"
>
<TabHeader
label={CONSTANTS.ITEM_VIEWER_TAB_TYPE.ORIGINAL}
value={CONSTANTS.ITEM_VIEWER_TAB_TYPE.ORIGINAL}
disabled={
!artifactsToTabs.includes(CONSTANTS.ITEM_VIEWER_TAB_TYPE.ORIGINAL)
}
/>
<TabHeader
label={CONSTANTS.ITEM_VIEWER_TAB_TYPE.PDF}
value={CONSTANTS.ITEM_VIEWER_TAB_TYPE.PDF}
disabled={!artifactsToTabs.includes(CONSTANTS.ITEM_VIEWER_TAB_TYPE.PDF)}
/>
<TabHeader
key={CONSTANTS.ITEM_VIEWER_TAB_TYPE.TEXT}
label={CONSTANTS.ITEM_VIEWER_TAB_TYPE.TEXT}
value={CONSTANTS.ITEM_VIEWER_TAB_TYPE.TEXT}
disabled={
!artifactsToTabs.includes(CONSTANTS.ITEM_VIEWER_TAB_TYPE.TEXT)
}
/>
<TabHeader
key={CONSTANTS.ITEM_VIEWER_TAB_TYPE.MARKUP}
label={CONSTANTS.ITEM_VIEWER_TAB_TYPE.MARKUP}
value={CONSTANTS.ITEM_VIEWER_TAB_TYPE.MARKUP}
disabled={!itemId}
/>
</TabList>
);
};
const ItemViewerTabPanelTabs: React.FC<{
itemId: number;
isHorizontal: boolean;
itemDefinitionId: number;
loading: boolean;
hasArtifacts: boolean;
isNativePdfViewer: boolean;
itemArtifacts: ItemArtifact[];
}> = ({
itemId,
isHorizontal,
itemDefinitionId,
loading,
hasArtifacts,
isNativePdfViewer,
itemArtifacts,
}) => {
if (!itemId) return <FaintInformational message="Please select a Document" />;
if (loading) return <div>Loading...</div>;
if (!hasArtifacts)
return (
<Informational
message="Document does not have any artifacts"
serverity="info"
/>
);
return (
<>
<ItemViewerTextTabPanel itemId={itemId} isHorizontal={isHorizontal} />
{!isNativePdfViewer && (
<ItemViewerOriginalTabPanel
itemId={itemId}
itemDefinitionId={itemDefinitionId}
isHorizontal={isHorizontal}
itemArtifacts={itemArtifacts}
/>
)}
</>
);
};
const ItemViewerOriginalTabPanel: React.FC<{
itemId: number;
isHorizontal: boolean;
itemDefinitionId: number;
itemArtifacts: ItemArtifact[];
}> = ({ itemId, itemDefinitionId, isHorizontal, itemArtifacts }) => {
const ref = useRef<HTMLDivElement>(null);
const theme = useTheme();
return (
<ItemViewerTabPanelContainer
isHorizontal={isHorizontal}
value={CONSTANTS.ITEM_VIEWER_TAB_TYPE.ORIGINAL}
ref={ref}
sx={{ backgroundColor: theme.palette.background.default }}
>
<ItemNativeViewer
itemId={itemId}
itemDefinitionId={itemDefinitionId}
containerRef={ref}
itemArtifacts={itemArtifacts ?? []}
/>
</ItemViewerTabPanelContainer>
);
};
const ItemViewerTextTabPanel: React.FC<{
itemId: number;
isHorizontal: boolean;
}> = ({ itemId, isHorizontal }) => {
return (
<ItemViewerTabPanelContainer
isHorizontal={isHorizontal}
value={CONSTANTS.ITEM_VIEWER_TAB_TYPE.TEXT}
>
<ItemTextViewer itemId={itemId} />
</ItemViewerTabPanelContainer>
);
};
const ItemViewerPdfTabPanel: React.FC<{
itemId: number;
isHorizontal: boolean;
show: boolean;
loading: boolean;
itemArtifacts: ItemArtifact[];
pdfViewerArtifactType: string;
editable: boolean;
}> = ({
itemId,
show,
isHorizontal,
loading,
pdfViewerArtifactType,
itemArtifacts,
editable,
}) => {
return (
<Box
sx={{
display: show ? 'block' : 'none',
height: isHorizontal ? '100%' : `calc(100% - 99px)`,
width: '100%',
overflow: 'hidden',
padding: 0,
}}
>
<ItemPdfViewer
itemId={itemId}
artifactType={pdfViewerArtifactType}
itemArtifacts={itemArtifacts}
editable={editable}
loading={loading}
/>
</Box>
);
};
const useItemArtifacts = (itemId: number, caseId: number) => {
return useQuery({
queryKey: ['ItemApi.getItemArtifacts', itemId, caseId],
queryFn: async ({ signal }) =>
await ItemApi.getItemArtifacts(itemId, caseId, { signal }),
});
};
const useCreateItemMarkupArtifact = (
itemId: number,
caseId: number,
enabled: boolean,
) => {
return useQuery({
queryKey: ['ItemApi.createItemMarkup', itemId, caseId],
queryFn: async ({ signal }) =>
await ItemApi.createItemMarkup(itemId, caseId, { signal }),
enabled,
});
};
Editor is loading...
Leave a Comment