Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/green-waves-marry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@zenml-io/react-component-library": patch
---

sidebar: start closing immediately when "close" button clicked, ignoring hover
4 changes: 2 additions & 2 deletions src/components/Sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import { useSidebarContext } from "./SidebarContext";

export const Sidebar = forwardRef<HTMLDivElement, HTMLProps<HTMLDivElement>>(
({ className, children, ...rest }, ref) => {
const { isOpen } = useSidebarContext();
const { isOpen, isClosing } = useSidebarContext();

return (
<nav
ref={ref}
className={cn(
`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`,
`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`,
className
)}
{...rest}
Expand Down
32 changes: 30 additions & 2 deletions src/components/Sidebar/SidebarContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import React, {
useState
} from "react";

import { Setter, getValueFromSetter } from "../../utilities";

type SidebarContextProps = {
isOpen: boolean;
isClosing: boolean;
setIsOpen: Dispatch<SetStateAction<boolean>>;
};

Expand All @@ -18,10 +21,35 @@ export function SidebarProvider({
children,
initialOpen = false
}: PropsWithChildren<{ initialOpen?: boolean }>) {
const [isOpen, setIsOpen] = useState(initialOpen);
const [isOpen, _setIsOpen] = useState(initialOpen);
const [isClosing, setIsClosing] = useState(false);

/**
* wrapper around `_setIsOpen`, to control `isClosing`.
* we should instead have a single "status" variable, but refactoring might suck..
*
* the purpose of `isClosing` is single:
* when sidebar is opened, and user hovers over it and clicks the button to close it,
* then the sidebar gets automatically closed, instead of waiting for the user
* to hover away from it before closing.
*/
function setIsOpen(newValue: Setter<boolean>): void {
_setIsOpen((currIsOpen) => {
const value: boolean = getValueFromSetter(newValue, currIsOpen);

if (value === false) {
setIsClosing(true);
setTimeout(() => setIsClosing(false), 300);
}

return value;
});
}

return (
<SidebarContext.Provider value={{ isOpen, setIsOpen }}>{children}</SidebarContext.Provider>
<SidebarContext.Provider value={{ isOpen, setIsOpen, isClosing }}>
{children}
</SidebarContext.Provider>
);
}

Expand Down
6 changes: 6 additions & 0 deletions src/utilities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ const customTwMerge = extendTailwindMerge({
export function cn(...inputs: ClassValue[]) {
return customTwMerge(clsx(inputs));
}

export type Setter<T> = T | ((prevValue: T) => T);

export function getValueFromSetter<T>(newValue: Setter<T>, currValue: T): T {
return newValue instanceof Function ? newValue(currValue) : newValue;
}
Loading