Feat: Add responsive workspace shell
This commit is contained in:
@@ -1,54 +1,38 @@
|
||||
import { createEffect, createSignal, createUniqueId, onCleanup, type JSX } from "solid-js";
|
||||
import { createUniqueId, Show, type JSX } from "solid-js";
|
||||
import { ProfileMenu } from "./ProfileMenu";
|
||||
import { UserNavButton } from "./UserNavButton";
|
||||
import { createDesktopMenuController } from "./createDesktopMenuController";
|
||||
import styles from "./UserNav.module.scss";
|
||||
|
||||
export const UserNav = (): JSX.Element => {
|
||||
const [isOpen, setIsOpen] = createSignal(false);
|
||||
type UserNavProps = {
|
||||
isMobileViewport: boolean;
|
||||
isMobileWorkspaceOpen: boolean;
|
||||
onToggleMobileWorkspace: VoidFunction;
|
||||
};
|
||||
|
||||
export const UserNav = (props: UserNavProps): JSX.Element => {
|
||||
const menuId = createUniqueId();
|
||||
let rootRef: HTMLDivElement | undefined;
|
||||
let menuRef: HTMLDivElement | undefined;
|
||||
|
||||
const closeMenu = (): void => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const toggleMenu = (): void => {
|
||||
setIsOpen((open) => !open);
|
||||
};
|
||||
|
||||
createEffect(() => {
|
||||
if (!isOpen()) return;
|
||||
|
||||
const handlePointerDown = (event: PointerEvent): void => {
|
||||
if (!rootRef) return;
|
||||
|
||||
const target = event.target;
|
||||
if (target instanceof Node && !rootRef.contains(target)) {
|
||||
closeMenu();
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent): void => {
|
||||
if (event.key === "Escape") {
|
||||
closeMenu();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("pointerdown", handlePointerDown);
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
menuRef?.querySelector<HTMLButtonElement>("[role='menuitem']")?.focus();
|
||||
|
||||
onCleanup(() => {
|
||||
document.removeEventListener("pointerdown", handlePointerDown);
|
||||
document.removeEventListener("keydown", handleKeyDown);
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<div class={styles.root} ref={rootRef}>
|
||||
<UserNavButton isOpen={isOpen()} menuId={menuId} onToggle={toggleMenu} />
|
||||
{isOpen() ? <ProfileMenu id={menuId} menuRef={(element) => (menuRef = element)} onSelect={closeMenu} /> : null}
|
||||
<Show
|
||||
when={props.isMobileViewport}
|
||||
fallback={<DesktopUserNav />}
|
||||
>
|
||||
<div class={styles.root}>
|
||||
<UserNavButton isOpen={props.isMobileWorkspaceOpen} menuId={menuId} onToggle={props.onToggleMobileWorkspace} />
|
||||
</div>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
const DesktopUserNav = (): JSX.Element => {
|
||||
const controller = createDesktopMenuController();
|
||||
const menuId = createUniqueId();
|
||||
|
||||
return (
|
||||
<div class={styles.root} ref={controller.setRootRef}>
|
||||
<UserNavButton isOpen={controller.isOpen()} menuId={menuId} onToggle={controller.toggleMenu} />
|
||||
{controller.isOpen() ? <ProfileMenu id={menuId} menuRef={controller.setMenuRef} onSelect={controller.closeMenu} /> : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user