From 5d5525ba4529a8564699616139cb715b4e5f78e4 Mon Sep 17 00:00:00 2001 From: reillylm Date: Thu, 12 Mar 2026 14:47:31 -0400 Subject: [PATCH] fix: make Sidebar scrollable Currently, if our viewport is too short, the overflow on the Sidebar gets cut off and we can't see the buttons at the bottom: Let's wrap this content in a `ScrollArea` so we can still see all the buttons on smaller viewports. We were previously using `ScrollArea` in the Nodes and Map pages, when filtering for devices by Role or Hardware. This keeps the logo on top and makes just the buttons scrollable. Previously, we had given the page-specific buttons (e.g. the channel list on the Messages page) `min-h-0`, I'm guessing with the intent of shrinking them if there's too many (like a flex-shrink situation), but that property wasn't having an effect. Even if it had worked, hiding the page-specific buttons seems undesirable (e.g. when you're on the Settings page, you wouldn't be able to see the "Configuration" buttons). Also, our viewport could still be too short to fit the other buttons, so scrolling would still ultimately be needed. Previously, we were hiding the `DeviceInfoPanel` buttons (`theme` / `commandMenu` / `language`) with `invisible` when the Sidebar is collapsed. However, `invisible` [makes the elements still take up space][1] (the `h-0` wasn't having an effect in that regard), which was making the Radix ScrollArea think it had scrollable content in those spots. Instead, let's hide with `hidden`. [1]: https://tailwindcss.com/docs/visibility#making-elements-invisible --- .../web/src/components/DeviceInfoPanel.tsx | 4 +- packages/web/src/components/Sidebar.tsx | 109 +++++++++--------- 2 files changed, 58 insertions(+), 55 deletions(-) diff --git a/packages/web/src/components/DeviceInfoPanel.tsx b/packages/web/src/components/DeviceInfoPanel.tsx index 5f3325e2..254ecd1e 100644 --- a/packages/web/src/components/DeviceInfoPanel.tsx +++ b/packages/web/src/components/DeviceInfoPanel.tsx @@ -212,7 +212,7 @@ export const DeviceInfoPanel = ({ "flex flex-col gap-2 mt-1", "transition-all duration-300 ease-in-out", isCollapsed - ? "opacity-0 max-w-0 h-0 invisible pointer-events-none" + ? "opacity-0 max-w-0 hidden pointer-events-none" : "opacity-100 max-w-xs h-auto visible", )} > @@ -244,7 +244,7 @@ export const DeviceInfoPanel = ({ "flex flex-col gap-1 mt-1", "transition-all duration-300 ease-in-out", isCollapsed - ? "opacity-0 max-w-0 h-0 invisible pointer-events-none" + ? "opacity-0 max-w-0 hidden pointer-events-none" : "opacity-100 max-w-xs visible", )} > diff --git a/packages/web/src/components/Sidebar.tsx b/packages/web/src/components/Sidebar.tsx index e79c67f4..e447c072 100644 --- a/packages/web/src/components/Sidebar.tsx +++ b/packages/web/src/components/Sidebar.tsx @@ -1,4 +1,5 @@ import { useFirstSavedConnection } from "@app/core/stores/deviceStore/selectors.ts"; +import { ScrollArea } from "@components/UI/ScrollArea.tsx"; import { SidebarButton } from "@components/UI/Sidebar/SidebarButton.tsx"; import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx"; import { Spinner } from "@components/UI/Spinner.tsx"; @@ -130,7 +131,7 @@ export const Sidebar = ({ children }: SidebarProps) => { return (
{
- - {pages.map((link) => { - return ( - { - if (myNode !== undefined) { - navigate({ to: `/${link.page}` }); - } + + + {pages.map((link) => { + return ( + { + if (myNode !== undefined) { + navigate({ to: `/${link.page}` }); + } + }} + active={link.page === pathname} + disabled={myNode === undefined} + /> + ); + })} + + +
{children}
+ +
+ {myNode === undefined ? ( +
+ + + {t("loading")} + +
+ ) : ( + setCommandPaletteOpen(true)} + setDialogOpen={() => setDialogOpen("deviceName", true)} + user={myNode.user} + firmwareVersion={myMetadata?.firmwareVersion ?? t("unknown.notAvailable")} + deviceMetrics={{ + batteryLevel: myNode.deviceMetrics?.batteryLevel, + voltage: + typeof myNode.deviceMetrics?.voltage === "number" + ? Math.abs(myNode.deviceMetrics?.voltage) + : undefined, }} - active={link.page === pathname} - disabled={myNode === undefined} + connectionStatus={activeConnection?.status} + connectionName={activeConnection?.name} /> - ); - })} - - -
{children}
- -
- {myNode === undefined ? ( -
- - - {t("loading")} - -
- ) : ( - setCommandPaletteOpen(true)} - setDialogOpen={() => setDialogOpen("deviceName", true)} - user={myNode.user} - firmwareVersion={myMetadata?.firmwareVersion ?? t("unknown.notAvailable")} - deviceMetrics={{ - batteryLevel: myNode.deviceMetrics?.batteryLevel, - voltage: - typeof myNode.deviceMetrics?.voltage === "number" - ? Math.abs(myNode.deviceMetrics?.voltage) - : undefined, - }} - connectionStatus={activeConnection?.status} - connectionName={activeConnection?.name} - /> - )} -
+ )} +
+
); };