Skip to content

Commit 2e3d1fb

Browse files
committed
feat(webapp): fade the org, project, env and help labels in while dragging the side menu open
The nav item labels already reveal in real time during a drag-open, but the org, project, environment and Help & Feedback labels were gated on the collapsed state, which only flips on release, so they stayed hidden for the whole drag and popped in at the end. Drive their width and opacity from the per frame --sm-label-opacity variable instead, exactly like the nav items, and keep those rows in their expanded arrangement while a drag is in progress so the revealing label has the layout it needs in both directions. Outside the resizable side menu the variable is unset and falls back to fully visible, so the environment selector and help popover render unchanged everywhere else.
1 parent e6cfa95 commit 2e3d1fb

3 files changed

Lines changed: 68 additions & 49 deletions

File tree

apps/webapp/app/components/navigation/EnvironmentSelector.tsx

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,15 @@ export function EnvironmentSelector({
4747
environment,
4848
className,
4949
isCollapsed = false,
50+
isDragging = false,
5051
}: {
5152
organization: MatchedOrganization;
5253
project: SideMenuProject;
5354
environment: SideMenuEnvironment;
5455
className?: string;
5556
isCollapsed?: boolean;
57+
/** True while the side menu is being drag-resized; keeps the row in its expanded arrangement. */
58+
isDragging?: boolean;
5659
}) {
5760
const { isManagedCloud } = useFeatures();
5861
const [isMenuOpen, setIsMenuOpen] = useState(false);
@@ -80,30 +83,33 @@ export function EnvironmentSelector({
8083
<PopoverTrigger
8184
className={cn(
8285
"group flex h-8 items-center rounded pl-1.75 hover:bg-background-hover focus-custom",
83-
isCollapsed ? "justify-center pr-0.5" : "justify-between pr-1",
86+
// The expanded row arrangement applies while dragging too — the resting classes only
87+
// flip on release, and the label reveal mid-drag needs the expanded layout.
88+
isDragging || !isCollapsed ? "justify-between pr-1" : "justify-center pr-0.5",
8489
className
8590
)}
8691
>
8792
<span className="flex min-w-0 flex-1 items-center gap-1.5 overflow-hidden">
8893
<EnvironmentIcon environment={environment} className="size-5 shrink-0" />
94+
{/*
95+
Width and opacity are driven by the resizable SideMenu's `--sm-label-opacity`
96+
variable so the label tracks a drag frame-by-frame in both directions (gating on
97+
isCollapsed, which only flips on release, made the label pop in after a drag-open).
98+
Unset in the other places this selector is used (blank-state panels, Limits page)
99+
→ falls back to fully visible.
100+
*/}
89101
<span
90-
className={cn(
91-
"flex min-w-0 items-center overflow-hidden transition-all duration-200",
92-
isCollapsed ? "max-w-0 opacity-0" : "max-w-[200px] opacity-100"
93-
)}
102+
className="flex min-w-0 items-center overflow-hidden"
103+
style={{
104+
maxWidth: "calc(var(--sm-label-opacity, 1) * 200px)",
105+
opacity: "var(--sm-label-opacity, 1)",
106+
}}
94107
>
95-
{/*
96-
Inner opacity is driven by the resizable SideMenu's `--sm-label-opacity`
97-
variable so the label fades as the menu is dragged narrower. Unset in the other
98-
places this selector is used (blank-state panels, Limits page) → falls back to 1.
99-
*/}
100-
<span className="flex min-w-0" style={{ opacity: "var(--sm-label-opacity, 1)" }}>
101-
<EnvironmentLabel
102-
environment={environment}
103-
className="text-[0.90625rem] font-medium tracking-[-0.01em]"
104-
disableTooltip
105-
/>
106-
</span>
108+
<EnvironmentLabel
109+
environment={environment}
110+
className="text-[0.90625rem] font-medium tracking-[-0.01em]"
111+
disableTooltip
112+
/>
107113
</span>
108114
</span>
109115
{/*

apps/webapp/app/components/navigation/HelpAndFeedbackPopover.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,18 @@ export function HelpAndFeedback({
7171
<span className="flex min-w-0 items-center gap-1.5 overflow-hidden">
7272
<QuestionMarkIcon className="size-5 min-w-5 shrink-0 text-success" />
7373
{/*
74-
Fades via the resizable side menu's `--sm-label-opacity` variable (falls back to
75-
fully visible when unset) and truncates as the menu narrows, matching the nav
76-
items. Only max-width transitions via CSS: the hover color snaps like the nav
77-
items, and transitioning opacity would lag the per-frame variable writes.
74+
Width and opacity follow the resizable side menu's `--sm-label-opacity` variable
75+
(falls back to fully visible when unset) so the label tracks a drag frame-by-frame
76+
in both directions, matching the nav items — gating the width on isCollapsed,
77+
which only flips on release, made the label pop in after a drag-open. No CSS
78+
transitions: they would lag the per-frame variable writes.
7879
*/}
7980
<span
80-
className={cn(
81-
"min-w-0 truncate text-[0.90625rem] font-medium tracking-[-0.01em] text-text-dimmed transition-[max-width] duration-150 group-hover:text-text-bright",
82-
isCollapsed ? "max-w-0" : "max-w-[150px]"
83-
)}
84-
style={{ opacity: "var(--sm-label-opacity, 1)" }}
81+
className="min-w-0 truncate text-[0.90625rem] font-medium tracking-[-0.01em] text-text-dimmed group-hover:text-text-bright"
82+
style={{
83+
maxWidth: "calc(var(--sm-label-opacity, 1) * 150px)",
84+
opacity: "var(--sm-label-opacity, 1)",
85+
}}
8586
>
8687
Help & Feedback
8788
</span>

apps/webapp/app/components/navigation/SideMenu.tsx

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,6 @@ const SIDE_MENU_PAD_X = `calc(0.625rem - 0.375rem * var(--sm-collapse, 0))`;
215215
* rail buttons inset symmetrically (matching the left padding) instead of touching the edge.
216216
*/
217217
const SIDE_MENU_SCROLL_PAD_RIGHT = `calc(0.25rem * var(--sm-collapse, 0))`;
218-
/** Applied to every fading label so it tracks --sm-label-opacity (falls back to fully visible). */
219-
const SIDE_MENU_LABEL_STYLE = { opacity: "var(--sm-label-opacity, 1)" } as const;
220218
/**
221219
* The selector rows' hover chevron: its 16px of layout width follows --sm-label-opacity so an
222220
* invisible chevron can never hold width mid-drag and push the row's overflow clip edge into the
@@ -226,6 +224,17 @@ const SIDE_MENU_LABEL_STYLE = { opacity: "var(--sm-label-opacity, 1)" } as const
226224
const SIDE_MENU_CHEVRON_STYLE = {
227225
maxWidth: "calc(var(--sm-label-opacity, 1) * 16px)",
228226
} as const;
227+
/**
228+
* The selector rows' label container (org/project/environment): width and opacity both follow
229+
* --sm-label-opacity so the label tracks a drag frame-by-frame in BOTH directions. Gating these on
230+
* `isCollapsed` (which only flips on release) made the labels pop in after a drag-open instead of
231+
* fading in like the nav items. The variable also animates during the click-toggle, and is 0/1 at
232+
* the collapsed/expanded resting states, so no isCollapsed classes or CSS transitions are needed.
233+
*/
234+
const SIDE_MENU_SELECTOR_LABEL_STYLE = {
235+
maxWidth: "calc(var(--sm-label-opacity, 1) * 200px)",
236+
opacity: "var(--sm-label-opacity, 1)",
237+
} as const;
229238

230239
function clamp(value: number, min: number, max: number) {
231240
return Math.min(max, Math.max(min, value));
@@ -639,11 +648,12 @@ export function SideMenu({
639648
/>
640649
<div className="absolute inset-0 grid grid-cols-[100%] grid-rows-[2.5rem_auto_1fr_auto] overflow-hidden">
641650
<div className="flex min-w-0 items-center overflow-hidden border-b border-transparent px-1 py-1">
642-
<div className={cn("min-w-0", !isCollapsed && "flex-1")}>
651+
<div className={cn("min-w-0", (isDragging || !isCollapsed) && "flex-1")}>
643652
<OrgSelector
644653
organizations={organizations}
645654
organization={organization}
646655
isCollapsed={isCollapsed}
656+
isDragging={isDragging}
647657
/>
648658
</div>
649659
<CollapsibleElement isDragging={isDragging}>
@@ -662,6 +672,7 @@ export function SideMenu({
662672
project={project}
663673
environment={environment}
664674
isCollapsed={isCollapsed}
675+
isDragging={isDragging}
665676
className="w-full"
666677
/>
667678
<div className="flex items-center">
@@ -670,6 +681,7 @@ export function SideMenu({
670681
project={project}
671682
environment={environment}
672683
isCollapsed={isCollapsed}
684+
isDragging={isDragging}
673685
className="min-w-0 flex-1"
674686
/>
675687
{environment.type === "DEVELOPMENT" && project.engine === "V2" && (
@@ -1139,10 +1151,13 @@ function OrgSelector({
11391151
organization,
11401152
organizations,
11411153
isCollapsed = false,
1154+
isDragging = false,
11421155
}: {
11431156
organization: MatchedOrganization;
11441157
organizations: MatchedOrganization[];
11451158
isCollapsed?: boolean;
1159+
/** True while the menu is being drag-resized; keeps the row in its expanded arrangement. */
1160+
isDragging?: boolean;
11461161
}) {
11471162
const currentPlan = useCurrentPlan();
11481163
const [isOrgMenuOpen, setOrgMenuOpen] = useState(false);
@@ -1167,21 +1182,18 @@ function OrgSelector({
11671182
<PopoverTrigger
11681183
className={cn(
11691184
"group flex h-8 items-center rounded pl-1.75 hover:bg-background-hover focus-custom",
1170-
isCollapsed ? "justify-center pr-0.5" : "w-full justify-between pr-1"
1185+
// The expanded row arrangement applies while dragging too — the resting classes only
1186+
// flip on release, and the label reveal mid-drag needs the expanded layout.
1187+
isDragging || !isCollapsed ? "w-full justify-between pr-1" : "justify-center pr-0.5"
11711188
)}
11721189
>
11731190
<span className="flex min-w-0 flex-1 items-center gap-1.5 overflow-hidden">
11741191
<Avatar avatar={organization.avatar} size={1.25} orgName={organization.title} />
11751192
<span
1176-
className={cn(
1177-
"flex min-w-0 items-center gap-1.5 overflow-hidden transition-all duration-200",
1178-
isCollapsed ? "max-w-0 opacity-0" : "max-w-[200px] opacity-100"
1179-
)}
1193+
className="flex min-w-0 items-center gap-1.5 overflow-hidden"
1194+
style={SIDE_MENU_SELECTOR_LABEL_STYLE}
11801195
>
1181-
<span
1182-
className="truncate text-[0.90625rem] font-medium tracking-[-0.01em] text-text-bright"
1183-
style={SIDE_MENU_LABEL_STYLE}
1184-
>
1196+
<span className="truncate text-[0.90625rem] font-medium tracking-[-0.01em] text-text-bright">
11851197
{organization.title}
11861198
</span>
11871199
</span>
@@ -1432,12 +1444,15 @@ function ProjectSelector({
14321444
organization,
14331445
environment,
14341446
isCollapsed = false,
1447+
isDragging = false,
14351448
className,
14361449
}: {
14371450
project: SideMenuProject;
14381451
organization: MatchedOrganization;
14391452
environment: SideMenuEnvironment;
14401453
isCollapsed?: boolean;
1454+
/** True while the menu is being drag-resized; keeps the row in its expanded arrangement. */
1455+
isDragging?: boolean;
14411456
className?: string;
14421457
}) {
14431458
const [isMenuOpen, setIsMenuOpen] = useState(false);
@@ -1454,24 +1469,21 @@ function ProjectSelector({
14541469
<PopoverTrigger
14551470
className={cn(
14561471
"group flex h-8 items-center rounded border pl-1.75 transition-[border-color] duration-150 hover:bg-background-hover focus-custom",
1457-
isCollapsed
1458-
? "justify-center border-transparent pr-0.5"
1459-
: "justify-between border-grid-bright pr-1",
1472+
// The expanded row arrangement applies while dragging too — the resting classes only
1473+
// flip on release, and the label reveal mid-drag needs the expanded layout.
1474+
isDragging || !isCollapsed
1475+
? "justify-between border-grid-bright pr-1"
1476+
: "justify-center border-transparent pr-0.5",
14601477
className
14611478
)}
14621479
>
14631480
<span className="flex min-w-0 flex-1 items-center gap-1.5 overflow-hidden">
14641481
<FolderOpenIcon className="size-5 shrink-0 text-text-bright" />
14651482
<span
1466-
className={cn(
1467-
"flex min-w-0 items-center overflow-hidden transition-all duration-200",
1468-
isCollapsed ? "max-w-0 opacity-0" : "max-w-[200px] opacity-100"
1469-
)}
1483+
className="flex min-w-0 items-center overflow-hidden"
1484+
style={SIDE_MENU_SELECTOR_LABEL_STYLE}
14701485
>
1471-
<span
1472-
className="truncate text-[0.90625rem] font-medium tracking-[-0.01em] text-text-bright"
1473-
style={SIDE_MENU_LABEL_STYLE}
1474-
>
1486+
<span className="truncate text-[0.90625rem] font-medium tracking-[-0.01em] text-text-bright">
14751487
{project.name ?? "Select a project"}
14761488
</span>
14771489
</span>

0 commit comments

Comments
 (0)