My last contribution to ChatCraft was a UI bug fix from last month, and I wrote about it and the project itself in this post. This week, I decided to revisit the project and continue contributing to some areas that I wanted to, but couldn't last time due to time constraints.
Table of Contents
1. Table of Contents
2. Enhancing the Sidebar
2.1. Implementing the enhancement 👨💻
2.2. Result 🎊
2.3. Pull Request
3. ZIndex Issue
3.1. Pull Request
4. Conclusion 🎇
5. Attribution
Enhancing the Sidebar
In the original layout, the Sidebar was placed in a GridItem within the main Grid that was being used for page layout. Whenever the sidebar was toggled, that GridItem was dynamically sized to reveal the element, otherwise its width was set to 0.
Here's what the layout looked like
And this is what the markup looked like.
<Grid
w="100%"
h="100%"
gridTemplateRows="min-content 1fr min-content"
gridTemplateColumns={{
base: isSidebarVisible ? "300px 1fr" : "0 1fr",
sm: isSidebarVisible ? "300px 1fr" : "0 1fr",
md: isSidebarVisible ? "minmax(300px, 1fr) 4fr" : "0: 1fr",
}}
bgGradient="linear(to-b, white, gray.100)"
_dark={{ bgGradient: "linear(to-b, gray.600, gray.700)" }}
>
...
<GridItem rowSpan={2} overflowY="auto">
<Sidebar selectedChat={chat} />
</GridItem>
...
...
</Grid>
Notice how the gridTemplateColumns are being set based on the visibility of the sidebar. While this is not bad, as a user of ChatCraft, I always felt an itch whenever I had to toggle the sidebar as it displaced the rest of the content when opening.
Not only that, but on smaller widths it squished the content to its right, which wouldn't leave a good impression on the users.
Even though other competitors use a similar approach of displacing the content for desktop sized screens, they do handle the mobile screens in a differnt manner.
Inspired by all this reasoning, I opened an issue to enhance the sidebar by allowing it to sit over the main content instead of pushing it away.
Implementing the enhancement 👨💻
I started out by planning how to add this behavior to sidebar while preserving the original Grid Layout as much as possible.
1. Changes to gridTemplateColumns
The first step was to modify the grid layout by fixing the widths assigned to grid columns.
<Grid
w="100%"
h="100%"
gridTemplateRows="min-content 1fr min-content"
gridTemplateColumns={"0 1fr"}
bgGradient="linear(to-b, white, gray.100)"
_dark={{ bgGradient: "linear(to-b, gray.600, gray.700)" }}
>
2. Placing Sidebar over Content
Now that toggling the sidebar didn't have any effect on sidebar column width, I started working on placing it over the main content when toggled. This involved wrapping the Sidebar
component in a Box that would handle all the positioning.
<GridItem rowSpan={2} colSpan={1}>
<Box
position="relative"
minWidth={"300px"}
width={"20vw"}
bgGradient="linear(to-b, white, gray.100)"
_dark={{ bgGradient: "linear(to-b, gray.600, gray.700)" }}
zIndex={theme.zIndices.modal}
overflowY="auto"
height={"100%"}
maxHeight={"100%"}
boxShadow={"base"}
>
<Sidebar selectedChat={chat} />
</Box>
</GridItem>
Let's break down what I did.
I set a position of relative, and a zIndex of from the default chakra theme.
This successfully placed the content in a higher stacking order, but some improvements were needed to styling since the sidebar itself was transparent, making the content underneath visible.
To fix this issue, I added the following theming properties to the sidebar wrapper, so it could blend well with the rest of the page.
bgGradient="linear(to-b, white, gray.100)"
_dark={{ bgGradient: "linear(to-b, gray.600, gray.700)" }}
boxShadow={"base"}
Here's the result.
The next challenge was to make the new sidebar scrollable when the content overflowed.
This was simple enough to fix, by setting the overflowY
to auto
.
overflowY="auto"
And now we have the scrollbar.
3. Adding toggle animation 🕹️
The last thing to handle was adding an open/close animation, as in its the then state, it was always visible.
It made sense to go with css-in-js approach and I came up with two keyframes animations, one for when the sidebar was opened and another when it was closed.
import { ...
keyframes,
} from "@chakra-ui/react";
// Sidebar Animations
const sidebarOpenAnimation = keyframes`
0% {
transform: scaleX(0);
}
50% {
transform: scaleX(1.075);
}
100% {
transform: scaleX(1);
}
`;
const sidebarClosedAnimation = keyframes`
from {
transform: scaleX(1);
}
to {
transform: scaleX(0);
}
`;
and added the following properties to the sidebar wrapper
transformOrigin={"left"}
transform={`translateX(${isSidebarVisible ? 0 : "-100%"})`}
animation={`${
isSidebarVisible ? sidebarOpenAnimation : sidebarClosedAnimation
} 150ms ease-out forwards`}
This made sure to trigger the open and close animations on the sidebar as required.
Sweet!
Now, one last problem was that whenever you refreshed the page, the close animation would trigger unnecessarily.
To fix this, I had to use an additional to check to only trigger sidebar animation when it was actually triggered
const [sidebarTouched, setSidebarTouched] = useState<boolean>(false);
const handleToggleSidebarVisible = useCallback(() => {
...
setSidebarTouched(true);
}, [isSidebarVisible, settings, setSettings, toggleSidebarVisible]);
<GridItem rowSpan={2} colSpan={1}>
<Box
position="relative"
...
transform={`translateX(${isSidebarVisible ? 0 : "-100%"})`}
animation={`${
isSidebarVisible // If visible, trigger open animation
? sidebarOpenAnimation
: // Only trigger closed animation when closed manually.
// This is because the sidebar open/close state is persisted across refreshes
// and may cause unnecessary animations if not handled
sidebarTouched
? sidebarClosedAnimation
: "none"
} 150ms ease-out forwards`}
>
<Sidebar selectedChat={chat} />
</Box>
</GridItem>
I used the transform property as a fallback for when the close animation was not triggered. I had to spend a lot of time in fixing this one, but I am sure there could be a better solution for this. Feel free to share in the comments!
Result 🎊
Now that we fixed all problems with the sidebar, let's see it in action.
Awesome!
Pull Request
All this progress, of course, was not part of a single commit.
Here's the pull request with all the converstaions and commits that went into this contribtion. It is still not merged as maintainers are not sure if they like it, will update the post when the decision is made.
ZIndex Issue
After working on the sidebar enhancement, I remembered there was a follow up issue from my last contribution where the model selection menu list required a higher z index.
Even though it looks a quick fix, I had to spend a while researching about the correct way assigning a z-index in such a project, that I have already discussed above.
Pull Request
After fixing the problem and testing locally, I opened another pull request which got merged fairly quickly as this was a small one.
Conclusion 🎇
In this post, I discussed about two of my latest contributions I made to ChatCraft (one tentative). Even though I thought the sidebar enhancement would be a quick one, it took quite a while to plan, implement and then debug the layout, while teaching me a couple of new things I didn't know about.
Overall, it was again a great and I would say, a fun learning experience.
Hope you liked the post and as always
Thanks for reading!
Attribution
Photo by Lautaro Andreani on Unsplash
Top comments (0)