Skip to content

Commit

Permalink
Feat(submodels):show submodel ID in viewer (#85)
Browse files Browse the repository at this point in the history
* feat(submodel-id): show id in tooltip and open dialog on click

---------

Co-authored-by: Nils Rothamel <[email protected]>
  • Loading branch information
XAlinaGS and NilsXitaso authored Jan 30, 2025
1 parent 93a2fbc commit 25acc5a
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/app/[locale]/viewer/[base64AasId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export default function Page() {
if (!submodelResponse.isSuccess)
return {
id: reference.keys[0].value,
error: 'Submodel failed to load', // TODO error localization
error: submodelResponse.errorCode,
};

return {
Expand Down
7 changes: 5 additions & 2 deletions src/app/[locale]/viewer/_components/SubmodelsOverviewCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { useIsMobile } from 'lib/hooks/UseBreakpoints';
import { SubmodelDetail } from './submodel/SubmodelDetail';
import { TabSelectorItem, VerticalTabSelector } from 'components/basics/VerticalTabSelector';
import { MobileModal } from 'components/basics/MobileModal';
import ErrorIcon from '@mui/icons-material/Error';
import InfoIcon from '@mui/icons-material/Info';
import LinkOffIcon from '@mui/icons-material/LinkOff';
import { SortNameplateElements } from 'app/[locale]/viewer/_components/submodel/sorting/SortNameplateElements';
import { SubmodelOrIdReference } from 'components/contexts/CurrentAasContext';
import ErrorBoundary from 'components/basics/ErrorBoundary';
Expand Down Expand Up @@ -43,12 +44,14 @@ export function SubmodelsOverviewCard({ submodelIds, submodelsLoading }: Submode
id: submodelId.id,
label: submodelId.submodel.idShort ?? '',
submodelData: submodelId.submodel,
startIcon: <InfoIcon/>,
};
} else {
return {
id: submodelId.id,
label: submodelId.id,
startIcon: <ErrorIcon />,
startIcon: <LinkOffIcon />,
submodelError: submodelId.error ?? ''
};
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Box, Dialog, DialogContent, IconButton, Typography } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import { useTranslations } from 'next-intl';

type SubmodelInfoDialogProps = {
readonly onClose: () => void;
readonly open: boolean;
readonly idShort: string | null | undefined ;
readonly id: string | undefined;
};

export function SubmodelInfoDialog(props: SubmodelInfoDialogProps) {
const t = useTranslations('submodels');

return (
<Dialog
open={props.open}
onClose={props.onClose}
maxWidth="md"
fullWidth={true}
>
<IconButton
aria-label="close"
onClick={props.onClose}
sx={{
position: 'absolute',
right: 8,
top: 8,
color: (theme) => theme.palette.grey[500],
}}
>
<CloseIcon />
</IconButton>
<DialogContent style={{ padding: '40px'}}>
<Box display="flex" flexDirection="column" gap="20px">
<Typography variant="h2" color={'primary'}>
{props.idShort}
</Typography>
<Box>
<Typography color="text.secondary">
{t('idLabel')} {props.id}
</Typography>
</Box>
</Box>
</DialogContent>
</Dialog>
);
}
69 changes: 57 additions & 12 deletions src/components/basics/VerticalTabSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import { alpha, Box, Button, styled, SvgIconProps, Typography } from '@mui/material';
import { alpha, Box, Button, styled, SvgIconProps, Tooltip, Typography } from '@mui/material';
import { ArrowForward } from '@mui/icons-material';
import { ReactElement } from 'react';
import { ReactElement, useState } from 'react';
import { Submodel } from '@aas-core-works/aas-core3.0-typescript/types';
import { tooltipText } from 'lib/util/ToolTipText';
import { SubmodelInfoDialog } from 'app/[locale]/viewer/_components/submodel/SubmodelInfoDialog';

export type TabSelectorItem = {
readonly id: string;
readonly label: string;
readonly startIcon?: ReactElement<SvgIconProps>;
readonly submodelData?: Submodel;
readonly submodelError?: string | Error;
};

type VerticalTabSelectorProps = {
readonly items: TabSelectorItem[];
readonly selected?: TabSelectorItem;
readonly hovered?: TabSelectorItem;
readonly setSelected?: (selected: TabSelectorItem) => void;
readonly setHovered?: (hovered: TabSelectorItem | undefined) => void;
};

const Tab = styled(Button)(({ theme }) => ({
Expand Down Expand Up @@ -44,35 +49,75 @@ const Tab = styled(Button)(({ theme }) => ({
'&:active': {
borderColor: 'transparent',
},
'&.Mui-disabled': {
pointerEvents: 'auto',
'&:hover': {
background: 'none',
},
},
}));

export function VerticalTabSelector(props: VerticalTabSelectorProps) {
const [submodelInfoDialogOpen, setSubmodelInfoDialogOpen] = useState(false);
const [hoveredItem, setHoveredItem] = useState<TabSelectorItem>();

const selectedCSSClass = (id: string) => (id === props.selected?.id ? 'selected' : '');

const handleSubmodelInfoModalClose = () => {
setSubmodelInfoDialogOpen(false);
}

return (
<Box sx={{ 'Button:nth-of-type(1)': { borderColor: 'transparent' } }}>
{props.items.map((item, index) => {
return (
<Tab
data-testid="submodel-tab"
key={index}
onMouseEnter={() => setHoveredItem(item)}
onMouseLeave={() => setHoveredItem(undefined)}
onClick={() => props.setSelected && props.setSelected(item)}
className={`tab-item ${selectedCSSClass(item.id)}`}
disabled={!!item.submodelError}
>
{item.startIcon ? (
<Box display="flex" alignItems="center">
<Box display="flex" alignItems="center" sx={{ mr: 1 }}>
{item.startIcon}
</Box>
<Typography>{item.label || ''}</Typography>
<Box display="flex" alignItems="left" style={{ whiteSpace: 'nowrap', paddingRight: '20px' }}>
<Typography>{tooltipText(item.label, 40) || ''}</Typography>
</Box>

<Box display="flex" alignItems="center" gap={2} >
<Box visibility={item.id === hoveredItem?.id ? 'visible' : 'hidden'}>
{item.submodelError ? (
<Tooltip title={item.submodelError.toString()}>
<Box display="flex"
sx={{ cursor: 'pointer' }}>
{item.startIcon}
</Box>
</Tooltip>
) : (
<Tooltip title={item.id.toString()}>
<Box display="flex"
sx={{ cursor: 'pointer' }}
onClick={(event) => {
setSubmodelInfoDialogOpen(true);
event.stopPropagation(); // don't open the tab
}}>
{item.startIcon}
</Box>
</Tooltip>
)}
</Box>
) : (
<Typography>{item.label || ''}</Typography>
)}
<ArrowForward color="primary" />
<ArrowForward color={item.submodelError ? 'disabled' : 'primary'} />
</Box>
</Tab>
);
})}
<SubmodelInfoDialog
open={submodelInfoDialogOpen}
onClose={handleSubmodelInfoModalClose}
id={props.selected?.id}
idShort={props.selected?.submodelData?.idShort}
/>
</Box>

);
}
1 change: 1 addition & 0 deletions src/locale/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"selectTimeframe": "Auswahl des Zeitabschnitts"
},
"renderError": "Teilmodell konnte nicht gerendert werden.",
"idLabel": "ID:",
"unknownModelType": "Unbekannter ModelType: {type}"
},
"qr-scanner": {
Expand Down
1 change: 1 addition & 0 deletions src/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"selectTimeframe": "Select timeframe"
},
"renderError": "Submodel can't be rendered.",
"idLabel": "ID:",
"unknownModelType": "Unknown ModelType: {type}"
},
"qr-scanner": {
Expand Down

0 comments on commit 25acc5a

Please sign in to comment.