Skip to content

Commit 68c37d6

Browse files
committed
fix: sidebar: start closing immediately when "close" button clicked, ignoring hover
1 parent 87d4145 commit 68c37d6

File tree

4 files changed

+43
-4
lines changed

4 files changed

+43
-4
lines changed

.changeset/green-waves-marry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@zenml-io/react-component-library": patch
3+
---
4+
5+
sidebar: start closing immediately when "close" button clicked, ignoring hover

src/components/Sidebar/Sidebar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ import { useSidebarContext } from "./SidebarContext";
1414

1515
export const Sidebar = forwardRef<HTMLDivElement, HTMLProps<HTMLDivElement>>(
1616
({ className, children, ...rest }, ref) => {
17-
const { isOpen } = useSidebarContext();
17+
const { isOpen, isClosing } = useSidebarContext();
1818

1919
return (
2020
<nav
2121
ref={ref}
2222
className={cn(
23-
`group flex-1 h-full flex w-9 ${isOpen ? "w-[220px]" : "hover:w-[220px]"} bg-neutral-100 transition-all overflow-x-hidden duration-300 flex-col items-center border-r border-theme-border-moderate`,
23+
`group flex-1 h-full flex w-9 ${isOpen ? "w-[220px]" : isClosing ? "" : "hover:w-[220px]"} bg-neutral-100 transition-all overflow-x-hidden duration-300 flex-col items-center border-r border-theme-border-moderate`,
2424
className
2525
)}
2626
{...rest}

src/components/Sidebar/SidebarContext.tsx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ import React, {
77
useState
88
} from "react";
99

10+
import { Setter, getValueFromSetter } from "../../utilities";
11+
1012
type SidebarContextProps = {
1113
isOpen: boolean;
14+
isClosing: boolean;
1215
setIsOpen: Dispatch<SetStateAction<boolean>>;
1316
};
1417

@@ -18,10 +21,35 @@ export function SidebarProvider({
1821
children,
1922
initialOpen = false
2023
}: PropsWithChildren<{ initialOpen?: boolean }>) {
21-
const [isOpen, setIsOpen] = useState(initialOpen);
24+
const [isOpen, _setIsOpen] = useState(initialOpen);
25+
const [isClosing, setIsClosing] = useState(false);
26+
27+
/**
28+
* wrapper around `_setIsOpen`, to control `isClosing`.
29+
* we should instead have a single "status" variable, but refactoring might suck..
30+
*
31+
* the purpose of `isClosing` is single:
32+
* when sidebar is opened, and user hovers over it and clicks the button to close it,
33+
* then the sidebar gets automatically closed, instead of waiting for the user
34+
* to hover away from it before closing.
35+
*/
36+
function setIsOpen(newValue: Setter<boolean>): void {
37+
_setIsOpen((currIsOpen) => {
38+
const value: boolean = getValueFromSetter(newValue, currIsOpen);
39+
40+
if (value === false) {
41+
setIsClosing(true);
42+
setTimeout(() => setIsClosing(false), 300);
43+
}
44+
45+
return value;
46+
});
47+
}
2248

2349
return (
24-
<SidebarContext.Provider value={{ isOpen, setIsOpen }}>{children}</SidebarContext.Provider>
50+
<SidebarContext.Provider value={{ isOpen, setIsOpen, isClosing }}>
51+
{children}
52+
</SidebarContext.Provider>
2553
);
2654
}
2755

src/utilities/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,9 @@ const customTwMerge = extendTailwindMerge({
2828
export function cn(...inputs: ClassValue[]) {
2929
return customTwMerge(clsx(inputs));
3030
}
31+
32+
export type Setter<T> = T | ((prevValue: T) => T);
33+
34+
export function getValueFromSetter<T>(newValue: Setter<T>, currValue: T): T {
35+
return newValue instanceof Function ? newValue(currValue) : newValue;
36+
}

0 commit comments

Comments
 (0)