feat: enables expandable labels on nodes

This commit is contained in:
Mark Mankarious
2023-11-09 01:16:44 +00:00
parent 37bc36c563
commit 36c5c179d5
6 changed files with 112 additions and 15 deletions

View File

@@ -40,7 +40,7 @@ export const IconSelectionControls = () => {
<Stack spacing={2}>
<Searchbox value={filter} onChange={setFilter} />
<Alert severity="info">
You can drag and drop icons below onto the canvas.
You can drag and drop items below onto the canvas.
</Alert>
</Stack>
</Section>

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { Button as MuiButton } from '@mui/material';
import { Button as MuiButton, SxProps } from '@mui/material';
import {
ExpandMore as ReadMoreIcon,
ExpandLess as ReadLessIcon
@@ -8,9 +8,10 @@ import {
interface Props {
isExpanded: boolean;
onClick: () => void;
sx?: SxProps;
}
export const ExpandButton = ({ isExpanded, onClick }: Props) => {
export const ExpandButton = ({ isExpanded, onClick, sx }: Props) => {
return (
<MuiButton
sx={{
@@ -21,7 +22,8 @@ export const ExpandButton = ({ isExpanded, onClick }: Props) => {
fontSize: '0.7em',
bottom: 5,
right: 5,
color: 'common.white'
color: 'common.white',
...sx
}}
onClick={onClick}
>

View File

@@ -0,0 +1,84 @@
import React, { useState, useRef, useEffect, useMemo } from 'react';
import { Box } from '@mui/material';
import { useResizeObserver } from 'src/hooks/useResizeObserver';
import { Gradient } from 'src/components/Gradient/Gradient';
import { ExpandButton } from './ExpandButton';
import { Label, Props as LabelProps } from './Label';
type Props = Omit<LabelProps, 'maxHeight'>;
const STANDARD_LABEL_HEIGHT = 80;
export const ExpandableLabel = ({ children, ...rest }: Props) => {
const [isExpanded, setIsExpanded] = useState(false);
const contentRef = useRef<HTMLDivElement>();
const { observe, size: contentSize } = useResizeObserver();
useEffect(() => {
if (!contentRef.current) return;
observe(contentRef.current);
}, [observe]);
const containerMaxHeight = useMemo(() => {
return isExpanded ? undefined : STANDARD_LABEL_HEIGHT;
}, [isExpanded]);
const isContentTruncated = useMemo(() => {
return !isExpanded && contentSize.height >= STANDARD_LABEL_HEIGHT - 10;
}, [isExpanded, contentSize.height]);
useEffect(() => {
contentRef.current?.scrollTo({ top: 0 });
}, [isExpanded]);
return (
<Label
{...rest}
maxHeight={containerMaxHeight}
maxWidth={isExpanded ? rest.maxWidth * 1.5 : rest.maxWidth}
>
<Box
ref={contentRef}
sx={{
'&::-webkit-scrollbar': {
display: 'none'
}
}}
style={{
overflowY: isExpanded ? 'scroll' : 'hidden',
maxHeight: containerMaxHeight
}}
>
{children}
{isContentTruncated && (
<Gradient
sx={{
position: 'absolute',
width: '100%',
height: 50,
bottom: 0,
left: 0
}}
/>
)}
</Box>
{((!isExpanded && isContentTruncated) || isExpanded) && (
<ExpandButton
sx={{
position: 'absolute',
bottom: 0,
right: 0,
m: 0.5
}}
isExpanded={isExpanded}
onClick={() => {
setIsExpanded(!isExpanded);
}}
/>
)}
</Label>
);
};

View File

@@ -3,7 +3,7 @@ import { Box, SxProps } from '@mui/material';
const CONNECTOR_DOT_SIZE = 3;
interface Props {
export interface Props {
labelHeight?: number;
maxWidth: number;
maxHeight?: number;

View File

@@ -1,11 +1,16 @@
import React, { useMemo } from 'react';
import { Box, Typography } from '@mui/material';
import { PROJECTED_TILE_SIZE, DEFAULT_LABEL_HEIGHT } from 'src/config';
import { Box, Typography, Stack } from '@mui/material';
import {
PROJECTED_TILE_SIZE,
DEFAULT_LABEL_HEIGHT,
MARKDOWN_EMPTY_VALUE
} from 'src/config';
import { getTilePosition } from 'src/utils';
import { useIcon } from 'src/hooks/useIcon';
import { ViewItem } from 'src/types';
import { useModelItem } from 'src/hooks/useModelItem';
import { Label } from 'src/components/Label/Label';
import { ExpandableLabel } from 'src/components/Label/ExpandableLabel';
import { MarkdownEditor } from 'src/components/MarkdownEditor/MarkdownEditor';
interface Props {
node: ViewItem;
@@ -26,7 +31,7 @@ export const Node = ({ node, order }: Props) => {
const description = useMemo(() => {
if (
modelItem.description === undefined ||
modelItem.description === '<p><br></p>'
modelItem.description === MARKDOWN_EMPTY_VALUE
)
return null;
@@ -52,16 +57,21 @@ export const Node = ({ node, order }: Props) => {
sx={{ position: 'absolute' }}
style={{ bottom: PROJECTED_TILE_SIZE.height / 2 }}
>
<Label
<ExpandableLabel
maxWidth={250}
maxHeight={100}
expandDirection="BOTTOM"
labelHeight={node.labelHeight ?? DEFAULT_LABEL_HEIGHT}
>
{modelItem.name && (
<Typography fontWeight={600}>{modelItem.name}</Typography>
)}
</Label>
<Stack spacing={1}>
{modelItem.name && (
<Typography fontWeight={600}>{modelItem.name}</Typography>
)}
{modelItem.description &&
modelItem.description !== MARKDOWN_EMPTY_VALUE && (
<MarkdownEditor value={modelItem.description} readOnly />
)}
</Stack>
</ExpandableLabel>
</Box>
)}
{iconComponent && (

View File

@@ -113,3 +113,4 @@ export const DEFAULT_ICON: Icon = {
export const DEFAULT_LABEL_HEIGHT = 20;
export const PROJECT_BOUNDING_BOX_PADDING = 3;
export const MARKDOWN_EMPTY_VALUE = '<p><br></p>';