Skip to content

Commit

Permalink
feat: enable resizing the side panel (#54)
Browse files Browse the repository at this point in the history
- The handle has a 5px+ border around it to help users select it
- Removed the code related to transitions as it wasn't working (transitions were activated on transform, transform wasn't used)
  • Loading branch information
tpatel authored Jun 13, 2023
1 parent 6a36820 commit 1eb74b7
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 60 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- New Avatar element to display avatars in messages
- AskFileMessage now supports multi file uploads (small breaking change)
- New settings interface including a new "Expand all" messages setting
- The element sidebar is resizable

### Fixed

Expand Down
63 changes: 63 additions & 0 deletions src/chainlit/frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/chainlit/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"react-hot-toast": "^2.4.0",
"react-hotkeys-hook": "^4.4.0",
"react-markdown": "^8.0.6",
"react-resizable": "^3.0.5",
"react-router-dom": "^6.8.1",
"react-string-replace": "^1.1.0",
"react-syntax-highlighter": "^15.5.0",
Expand All @@ -45,6 +46,7 @@
"@types/node": "^20.0.0",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@types/react-resizable": "^3.0.4",
"@types/react-syntax-highlighter": "^15.5.6",
"@types/react-window-infinite-loader": "^1.0.6",
"@typescript-eslint/eslint-plugin": "^5.59.2",
Expand Down
152 changes: 92 additions & 60 deletions src/chainlit/frontend/src/components/element/sideView.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,55 @@
import CloseIcon from '@mui/icons-material/Close';
import { IconButton, Box, BoxProps, Typography, Stack } from '@mui/material';
import { styled, Theme, CSSObject } from '@mui/material/styles';
import { styled } from '@mui/material/styles';
import { renderElement } from 'components/element/view';
import { useMemo } from 'react';
import { forwardRef, useMemo, useState } from 'react';
import { useRecoilState } from 'recoil';
import { sideViewState } from 'state/element';

const drawerWidth = 400;

const openedMixin = (theme: Theme): CSSObject => ({
padding: '1.5rem',
paddingTop: '.5rem',
width: drawerWidth,
transition: theme.transitions.create('transform', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen
}),
overflowX: 'hidden'
});

const closedMixin = (theme: Theme): CSSObject => ({
transition: theme.transitions.create('transform', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}),
overflowX: 'hidden',
width: 0
});
import { Resizable } from 'react-resizable';
import 'react-resizable/css/styles.css';

interface DrawerProps extends BoxProps {
open?: boolean;
width?: number | string;
}

const Handle = forwardRef(function Handle(
{ ...props }: { handleAxis?: string },
ref
) {
// Make sure the dom element doesn't receive the handleAxis prop.
delete props.handleAxis;
return (
<Box
sx={{
width: '4px',
height: '24px',
position: 'absolute',
top: '50%',
marginTop: '-12px',
left: 0,
cursor: 'ew-resize',
padding: '10px 5px'
}}
ref={ref}
{...props}
>
<Box
sx={{
width: '100%',
height: '100%',
backgroundColor: 'grey.300',
borderRadius: '4px'
}}
/>
</Box>
);
});

const Drawer = styled(Box, {
shouldForwardProp: (prop) => prop !== 'open'
})<DrawerProps>(({ theme, open }) => ({
})<DrawerProps>(({ theme, open, width }) => ({
width,
backgroundColor:
theme.palette.mode === 'dark'
? theme.palette.grey[800]
Expand All @@ -44,23 +59,26 @@ const Drawer = styled(Box, {
? theme.palette.grey[800]
: theme.palette.grey[200]
}`,
display: 'flex',
display: open ? 'flex' : 'none',
flexDirection: 'column',
borderRadius: 0,
flexShrink: 0,
color: theme.palette.text.primary,
whiteSpace: 'nowrap',
boxSizing: 'border-box',
...(open && {
...openedMixin(theme)
}),
...(!open && {
...closedMixin(theme)
})
padding: '1.5rem',
paddingTop: '.5rem',
overflowX: 'hidden'
}));

const SideView = () => {
const [sideViewElement, setSideViewElement] = useRecoilState(sideViewState);
const [resizeInProgress, setResizeInProgress] = useState(false);
const [drawerWidth, setDrawerWidth] = useState<number>(400);

const handleResize = (event: any, data: { size: { width: number } }) => {
setDrawerWidth(data.size.width);
};

const element = useMemo(() => {
if (sideViewElement) {
Expand All @@ -70,35 +88,49 @@ const SideView = () => {
}, [sideViewElement]);

return (
<Drawer open={!!sideViewElement}>
<Stack direction="row" alignItems="center">
<Typography
noWrap
fontSize="20px"
fontWeight={500}
id="side-view-title"
>
{sideViewElement?.name}
</Typography>
<IconButton
edge="end"
sx={{ ml: 'auto' }}
onClick={() => setSideViewElement(undefined)}
>
<CloseIcon />
</IconButton>
</Stack>
<Resizable
width={drawerWidth}
height={0} // Resizable requires height, but we don't want to limit height, so set it to 0.
onResize={handleResize}
onResizeStart={() => setResizeInProgress(true)}
onResizeStop={() => setResizeInProgress(false)}
resizeHandles={['w']}
handle={<Handle />}
axis="x"
minConstraints={[100, 0]} // Minimum width of 100px and no limit on height.
maxConstraints={[1000, 0]} // Maximum width of 1000px and no limit on height.
>
<Drawer open={!!sideViewElement} width={drawerWidth}>
<Stack direction="row" alignItems="center">
<Typography
noWrap
fontSize="20px"
fontWeight={500}
id="side-view-title"
>
{sideViewElement?.name}
</Typography>
<IconButton
edge="end"
sx={{ ml: 'auto' }}
onClick={() => setSideViewElement(undefined)}
>
<CloseIcon />
</IconButton>
</Stack>

<Box
mt="1.5rem"
id="side-view-content"
sx={{
height: '100%'
}}
>
{element}
</Box>
</Drawer>
<Box
mt="1.5rem"
id="side-view-content"
sx={{
height: '100%',
pointerEvents: resizeInProgress ? 'none' : 'auto'
}}
>
{element}
</Box>
</Drawer>
</Resizable>
);
};

Expand Down

0 comments on commit 1eb74b7

Please sign in to comment.